home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / send.c < prev    next >
C/C++ Source or Header  |  1996-03-16  |  178KB  |  6,353 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: send.c,v 4.348 1996/03/17 00:23:16 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.        send.c
  44.        Functions for composing and sending mail
  45.  
  46.  ====*/
  47.  
  48. #include "headers.h"
  49. #include "../c-client/smtp.h"
  50. #include "../c-client/nntp.h"
  51.  
  52.  
  53. #ifndef TCPSTREAM
  54. #define TCPSTREAM void
  55. #endif
  56.  
  57. #define    MIME_VER    "MIME-Version: 1.0\015\012"
  58.  
  59.  
  60. /*
  61.  * Internal Prototypes
  62.  */
  63. int       redraft PROTO((CONTEXT_S *, char *, ENVELOPE **, BODY **, char **,
  64.            char **, char **, PINEFIELD **));
  65. void       redraft_cleanup PROTO((char *, char *, MAILSTREAM *));
  66. void       outgoing2strings PROTO((METAENV *, BODY *, void **, PATMT **));
  67. void       strings2outgoing PROTO((METAENV *, BODY **, PATMT *));
  68. void       resolve_encoded_entries PROTO((ADDRESS *, ADDRESS *));
  69. int        write_fcc PROTO((char *, CONTEXT_S *, STORE_S *, char *));
  70. void       create_message_body PROTO((BODY **, PATMT *));
  71. int       check_addresses PROTO((METAENV *));
  72. ADDRESS   *generate_from PROTO(());
  73. void       free_attachment_list PROTO((PATMT **));
  74. PINEFIELD *get_dflt_custom_hdrs PROTO(());
  75. void       free_customs PROTO((PINEFIELD *));
  76. int        view_as_rich PROTO((char *, int));
  77. int        count_custom_hdrs PROTO((void));
  78. int        is_a_forbidden_hdr PROTO((char *));
  79. int        is_a_std_hdr PROTO((char *));
  80. void       set_default_hdrval PROTO((PINEFIELD *));
  81. void       customized_hdr_setup PROTO((PINEFIELD *));
  82. void       update_message_id PROTO((ENVELOPE *, char *));
  83. int       filter_message_text PROTO((char *, ENVELOPE *, BODY *, STORE_S **));
  84. long       message_format_for_pico PROTO((long, int (*)(int)));
  85. char      *send_exit_for_pico PROTO(());
  86. int       mime_type_for_pico PROTO((char *));
  87. void       pine_write_body_header PROTO((char **, BODY  *));
  88. void       pine_encode_body PROTO((BODY *));
  89. int        pine_rfc822_header PROTO((METAENV *, BODY *, soutr_t, TCPSTREAM *));
  90. int        pine_header_line PROTO((char *, char *, METAENV *, char *, soutr_t,
  91.                 TCPSTREAM *, int, int));
  92. int       pine_address_line PROTO((char *, char *, METAENV *, ADDRESS *,
  93.                  soutr_t, TCPSTREAM *, int, int));
  94. char      *prune_personal PROTO((ADDRESS *, char *));
  95. int        post_rfc822_output PROTO ((char *, ENVELOPE *, BODY *, soutr_t,
  96.                       TCPSTREAM *));
  97. int        pine_rfc822_output PROTO ((METAENV *, BODY *, soutr_t,TCPSTREAM *));
  98. long       pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
  99. void       pine_free_body PROTO((BODY **));
  100. void       pine_free_body_data PROTO((BODY *));
  101. void       pine_free_body_part PROTO((PART **));
  102. long       pine_smtp_verbose PROTO((SMTPSTREAM *));
  103. void       pine_smtp_verbose_out PROTO((char *));
  104. int        call_mailer PROTO((METAENV *, BODY *));
  105. int        news_poster PROTO((METAENV *, BODY *));
  106. char      *tidy_smtp_mess PROTO((char *, char *, char *));
  107. char      *mime_stats PROTO((BODY *));
  108. void       mime_recur PROTO((BODY *));
  109. int        open_fcc PROTO((char *, CONTEXT_S **, int));
  110. long       pine_pipe_soutr PROTO((void *, char *));
  111. char      *pine_pipe_getline PROTO((void *));
  112. void       pine_pipe_close PROTO((void *));
  113. int       sent_percent PROTO(());
  114. long       send_body_size PROTO((BODY *));
  115. void       set_body_size PROTO((BODY *));
  116. int       troll_8bit PROTO((BODY *));
  117.  
  118.  
  119. /*
  120.  * Buffer to hold pointers into pine data that's needed by pico. 
  121.  * Defined here so as not to take up room on the stack.  Better malloc'd?
  122.  */
  123. static    PICO    pbuf;
  124.  
  125.  
  126. /* 
  127.  * Storage object where the FCC (or postponed msg) is to be written.
  128.  * This is amazingly bogus.  Much work was done to put messages 
  129.  * together and encode them as they went to the tmp file for sendmail
  130.  * or into the SMTP slot (especially for for DOS, to prevent a temporary
  131.  * file (and needlessly copying the message).
  132.  * 
  133.  * HOWEVER, since there's no piping into c-client routines
  134.  * (particularly mail_append() which copies the fcc), the fcc will have
  135.  * to be copied to disk.  This global tells pine's copy of the rfc822
  136.  * output functions where to also write the message bytes for the fcc.
  137.  * With piping in the c-client we could just have two pipes to shove
  138.  * down rather than messing with damn copies.  FIX THIS!
  139.  *
  140.  * The function open_fcc, locates the actual folder and creates it if
  141.  * requested before any mailing or posting is done.
  142.  */
  143. static STORE_S *local_so      = NULL;
  144. static int    local_written = 0;
  145.  
  146.  
  147. /*
  148.  * Locally global pointer to stream used for sending/posting.
  149.  * It's also used to indicate when/if we write the Bcc: field in
  150.  * the header.
  151.  */
  152. static SMTPSTREAM *sending_stream = NULL;
  153. static FILE      *verbose_send_output = NULL;
  154. static long       send_bytes_sent, send_bytes_to_send;
  155. static char      *sending_filter_requested;
  156. static int       verbose_requested;
  157. static METAENV      *send_header = NULL;
  158.  
  159.  
  160. #ifdef    DOS
  161. #define    FCC_SOURCE    FileStar
  162. #define    FCC_STREAM_MODE    OP_SHORTCACHE
  163. #else
  164. #define    FCC_SOURCE    CharStar
  165. #define    FCC_STREAM_MODE    0L
  166. #endif
  167.  
  168.  
  169. #define    INTRPT_PMT \
  170.         "Continue INTERRUPTED composition (answering \"n\" won't erase it)"
  171. #define    PSTPND_PMT \
  172.         "Continue postponed composition (answering \"No\" won't erase it)"
  173. #define    POST_PMT   \
  174.         "Posted message may go to thousands of readers. Really post"
  175. #define    INTR_DEL_FAIL    \
  176.        "Undelete messages to remain postponed, and then continue message"
  177. #define    INTR_DEL_PMT    "Deleted messages will be removed from folder.  Delete"
  178.  
  179. /*
  180.  * Since c-client preallocates, it's necessary here to define a limit
  181.  * such that we don't blow up in c-client (see rfc822_address_line()).
  182.  */
  183. #define    MAX_SINGLE_ADDR    MAILTMPLEN
  184.  
  185.  
  186. #ifdef    DEBUG_PLUS
  187. #include    <sys/timeb.h>
  188. #define    TIME_STAMP(str, l)    { \
  189.                   struct timeb tb; \
  190.                   ftime(&tb); \
  191.                   dprint(l, (debugfile, \
  192.                          "\nKACHUNK (%s) : %.8s.%3.3d\n", \
  193.                          str, ctime(&tb.time) + 11, \
  194.                          tb.millitm))\
  195.                 }
  196. #else
  197. #define    TIME_STAMP(str, l)
  198. #endif
  199.  
  200.  
  201.  
  202. /*----------------------------------------------------------------------
  203.     Compose screen (not forward or reply). Set up envelope, call composer
  204.   
  205.    Args: pine_state -- The usual pine structure
  206.  
  207.   Little front end for the compose screen
  208.   ---*/
  209. void
  210. compose_screen(pine_state)
  211.     struct pine *pine_state;
  212. {
  213.     ps_global = pine_state;
  214.     mailcap_free(); /* free resources we won't be using for a while */
  215.     pine_state->next_screen = pine_state->prev_screen;
  216.     compose_mail(NULL, NULL, NULL);
  217. }
  218.  
  219.  
  220.  
  221. /*----------------------------------------------------------------------
  222.      Format envelope for outgoing message and call editor
  223.  
  224.  Args:  given_to -- An address to send mail to (usually from command line 
  225.                        invocation)
  226.         fcc_arg  -- The fcc that goes with this address.
  227.  
  228.  If a "To" line is given format that into the envelope and get ready to call
  229.            the composer
  230.  If there's a message postponed, offer to continue it, and set it up,
  231.  otherwise just fill in the outgoing envelope as blank.
  232.  
  233.  NOTE: we ignore postponed and interrupted messages in nr mode
  234.  ----*/
  235. void 
  236. compose_mail(given_to, fcc_arg, inc_text_getc)
  237.     char    *given_to,
  238.         *fcc_arg;
  239.     gf_io_t  inc_text_getc;
  240. {
  241.     BODY      *body;
  242.     ENVELOPE  *outgoing = NULL;
  243.     PINEFIELD *custom   = NULL;
  244.     char      *p,
  245.           *fcc_to_free,
  246.           *fcc      = NULL,
  247.           *refs     = NULL,
  248.           *lcc      = NULL,
  249.           *sig      = NULL;
  250.     int        fcc_is_sticky = 0;
  251.  
  252.     dprint(1, (debugfile,
  253.                  "\n\n    ---- COMPOSE SCREEN (not in pico yet) ----\n"));
  254.  
  255.     /*-- Check for INTERRUPTED mail  --*/
  256.     if(!ps_global->anonymous && !given_to){
  257.     char         file_path[MAXPATH+1];
  258.     int         ret = 'n';
  259.  
  260.     /* build filename and see if it exists.  build_path creates
  261.      * an explicit local path name, so all c-client access is thru
  262.      * local drivers.
  263.      */
  264.     file_path[0] = '\0';
  265.     build_path(file_path,
  266.            ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  267.                        : ps_global->home_dir,
  268.            INTERRUPTED_MAIL);
  269.  
  270.     /* check to see if the folder exists, the user wants to continue
  271.      * and that we can actually read something in...
  272.      */
  273.     if(folder_exists("[]", file_path) > 0
  274.        && (ret = want_to(INTRPT_PMT, 'y', 'x', NO_HELP, 0, 0)) == 'y'
  275.        && !redraft(NULL, file_path, &outgoing, &body, &fcc,
  276.                &refs, &lcc, &custom)){
  277.         return;
  278.     }
  279.     else if(ret == 'x'){
  280.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  281.         return;
  282.     }
  283.     }
  284.  
  285.     /*-- Check for postponed mail --*/
  286.     if(!outgoing                /* not replying/forwarding */
  287.        && !ps_global->anonymous            /* not special anon mode */
  288.        && !given_to                /* not command line send */
  289.        && ps_global->VAR_POSTPONED_FOLDER    /* folder to look in */
  290.        && ps_global->VAR_POSTPONED_FOLDER[0]){
  291.     CONTEXT_S   *p_cntxt;
  292.     int         ret = 'n';
  293.  
  294.     /* find default context to look for folder */
  295.     if(!(p_cntxt = default_save_context(ps_global->context_list)))
  296.       p_cntxt = ps_global->context_list;
  297.  
  298.     /* check to see if the folder exists, the user wants to continue
  299.      * and that we can actually read something in...
  300.      */
  301.     if(folder_exists(p_cntxt ? p_cntxt->context : "[]", 
  302.              ps_global->VAR_POSTPONED_FOLDER) > 0
  303.        && (ret = want_to(PSTPND_PMT, 'y', 'x', NO_HELP, 0, 0)) == 'y'
  304.        && !redraft(p_cntxt, ps_global->VAR_POSTPONED_FOLDER,
  305.                &outgoing, &body, &fcc, &refs, &lcc, &custom)){
  306.         return;
  307.     }
  308.     else if(ret == 'x'){
  309.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  310.         return;
  311.     }
  312.     }
  313.  
  314.     /*-- normal composition --*/
  315.     if(!outgoing){
  316.         /*=================  Compose new message ===============*/
  317.         body         = mail_newbody();
  318.         outgoing     = mail_newenvelope();
  319.  
  320.         if(given_to)
  321.       rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
  322.  
  323.         outgoing->message_id = generate_message_id(ps_global);
  324.  
  325.     /*
  326.      * The type of storage object allocated below is vitally
  327.      * important.  See SIMPLIFYING ASSUMPTION #37
  328.      */
  329.     if(body->contents.binary = (void *)so_get(PicoText,NULL,EDIT_ACCESS)){
  330.         char ch;
  331.  
  332.         if(inc_text_getc){
  333.         while((*inc_text_getc)(&ch))
  334.           if(!so_writec(ch, (STORE_S *)body->contents.binary)){
  335.               break;
  336.           }
  337.         }
  338.     }
  339.     else{
  340.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  341.                  "Problem creating space for message text.");
  342.         return;
  343.     }
  344.  
  345.     if(sig = get_signature()){
  346.         if(*sig)
  347.           so_puts((STORE_S *)body->contents.binary, sig);
  348.  
  349.         fs_give((void **)&sig);
  350.     }
  351.  
  352.     body->type = TYPETEXT;
  353.     }
  354.  
  355.     ps_global->prev_screen = compose_screen;
  356.     if(!(fcc_to_free = fcc))        /* Didn't pick up fcc, use given  */
  357.       fcc = fcc_arg;
  358.  
  359.     /*
  360.      * check whether a build_address-produced fcc is different from
  361.      * fcc.  If same, do nothing, if different, set sticky bit in pine_send.
  362.      */
  363.     if(outgoing->to){
  364.     char *tmp_fcc;
  365.  
  366.     tmp_fcc = get_fcc_based_on_to(outgoing->to);
  367.     if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
  368.       fcc_is_sticky++;  /* cause sticky bit to get set */
  369.  
  370.     if(tmp_fcc)
  371.       fs_give((void **)&tmp_fcc);
  372.     }
  373.  
  374.     pine_send(outgoing, &body, "COMPOSE MESSAGE", fcc, NULL, refs, NULL, lcc,
  375.           custom, fcc_is_sticky);
  376.  
  377.     if(fcc_to_free)
  378.       fs_give((void **)&fcc_to_free);
  379.  
  380.     if(lcc)
  381.       fs_give((void **)&lcc);
  382.  
  383.     mail_free_envelope(&outgoing);
  384.     pine_free_body(&body);
  385. }
  386.  
  387.  
  388.  
  389. /*----------------------------------------------------------------------
  390.     Given context and folder, offer the context for redraft...
  391.  
  392.  Args:    cntxt -- 
  393.     mbox -- 
  394.     outgoing -- 
  395.     body -- 
  396.     fcc -- 
  397.     custom -- 
  398.  
  399.  ----*/
  400. int
  401. redraft(cntxt, mbox, outgoing, body, fcc, ref_list, lcc, custom)
  402.     CONTEXT_S  *cntxt;
  403.     char       *mbox;
  404.     ENVELOPE  **outgoing;
  405.     BODY      **body;
  406.     char      **fcc;
  407.     char      **ref_list;
  408.     char      **lcc;
  409.     PINEFIELD **custom;
  410. {
  411.     MAILSTREAM    *stream;
  412.     ENVELOPE    *e = NULL;
  413.     BODY    *b;
  414.     PART        *part;
  415.     STORE_S    *so = NULL;
  416.     PINEFIELD   *pf;
  417.     gf_io_t     pc;
  418.     char    *extras, **fields, **values, *c_string, *p;
  419.     long     cont_msg = 1L;
  420.     int         i, j, rv = 0, pine_generated = 0;
  421. #define    TMP_BUF1    (tmp_20k_buf)
  422. #define    TMP_BUF2    (tmp_20k_buf + MAXPATH)
  423.  
  424.     /*
  425.      * if we have a context AND we're looking at an ambiguous name, 
  426.      * set the context string.  Otherwise, our context "relativeness"
  427.      * code in context.c may cause the fully qualified name to still
  428.      * get interpreted relative to the provided context...
  429.      */
  430.     c_string = (cntxt && context_isambig(mbox)) ? cntxt->context : "[]";
  431.  
  432.     /*
  433.      * Need to check here to see if the folder being opened for redraft
  434.      * is the currently opened folder.  If so, just use the current
  435.      * mail_stream...
  436.      */
  437.     context_apply(TMP_BUF1, c_string, mbox);
  438.     if(context_isambig(ps_global->cur_folder))
  439.       context_apply(TMP_BUF2, ps_global->context_current->context,
  440.             ps_global->cur_folder);
  441.     else
  442.       strcpy(TMP_BUF2, ps_global->cur_folder);
  443.  
  444.     if(!strcmp(TMP_BUF1, TMP_BUF2)){
  445.     stream = ps_global->mail_stream;
  446.     }
  447.     else{
  448.     if((stream = context_open(c_string, NULL, mbox, FCC_STREAM_MODE))
  449.        && (extras = strindex(stream->mailbox, '<'))
  450.        && !strcmp(extras + 1, "no_mailbox>"))
  451.       stream = NULL;
  452.     }
  453.  
  454.     if(stream){
  455.     /*
  456.      * If the we're manipulating the current folder, don't bother
  457.      * with index
  458.      */
  459.     if(!stream->nmsgs){
  460.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  461.                  "Empty folder.  No messages really postponed!");
  462.         redraft_cleanup(c_string, mbox, stream);
  463.         return(0);
  464.     }
  465.     else if(stream == ps_global->mail_stream){
  466.         /*
  467.          * Since the user's got this folder already opened and they're
  468.          * on a selected message, pick that one rather than rebuild
  469.          * another index screen...
  470.          */
  471.         cont_msg = mn_m2raw(ps_global->msgmap,
  472.                 mn_get_cur(ps_global->msgmap));
  473.     }
  474.     else if(stream->nmsgs > 1L){        /* offer browser ? */
  475.         MSGNO_S *msgmap = NULL;
  476.  
  477.         mn_init(&msgmap, stream->nmsgs);
  478.         mn_set_cur(msgmap, 1L);
  479.  
  480.         clear_index_cache();
  481.         while(1){
  482.         rv = index_lister(ps_global, cntxt, mbox, stream, msgmap);
  483.         cont_msg = mn_get_cur(msgmap);
  484.         if(count_flagged(stream, "DELETED")
  485.            && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, 0, 0) == 'n'){
  486.             q_status_message(SM_ORDER, 3, 3, INTR_DEL_FAIL);
  487.             continue;
  488.         }
  489.  
  490.         break;
  491.         }
  492.  
  493.         clear_index_cache();
  494.         mn_give(&msgmap);
  495.  
  496.         if(rv){
  497.         redraft_cleanup(c_string, mbox, stream);
  498.         q_status_message(SM_ORDER, 0, 3, "Composition cancelled");
  499.         return(0);
  500.         }
  501.     }
  502.  
  503.     if((e = mail_fetchstructure(stream, cont_msg, &b))
  504.        && (so = (void *)so_get(PicoText, NULL, EDIT_ACCESS))){
  505.         gf_set_so_writec(&pc, so);
  506.  
  507.         *custom = get_dflt_custom_hdrs();
  508.         i       = (count_custom_hdrs() + 4) * sizeof(char *);
  509.  
  510.         /*
  511.          * Having these two fields separated isn't the slickest, but
  512.          * converting the pointer array for fetchheader_lines() to
  513.          * a list of structures or some such for simple_header_parse()
  514.          * is too goonie.  We could do something like reuse c-client's
  515.          * PARAMETER struct which is a simple char * pairing, but that
  516.          * doesn't make sense to pass to fetchheader_lines()...
  517.          */
  518.         fields = (char **) fs_get((size_t) i * sizeof(char *));
  519.         values = (char **) fs_get((size_t) i * sizeof(char *));
  520.         memset(fields, 0, (size_t) i * sizeof(char *));
  521.         memset(values, 0, (size_t) i * sizeof(char *));
  522.  
  523.         fields[i = 0] = "Fcc";        /* Fcc: special case */
  524.         fields[++i]   = "References";    /* References: too... */
  525.         fields[++i]   = "Lcc";        /* Lcc: too... */
  526.  
  527.         /*
  528.          * BOGUS ALERT!  A c-client DEFICIENCY doesn't give us the
  529.          * newsgroups if the folder's accessed via IMAP, so if
  530.          * fetchstructure says there aren't any, we need to go to 
  531.          * the trouble of checking ourselves.  Sheesh.
  532.          */
  533.         if(!e->newsgroups)
  534.           fields[++i] = "Newsgroups";
  535.  
  536.         for(pf = *custom, ++i; pf && pf->name; pf = pf->next, i++)
  537.           fields[i] = pf->name;        /* assign custom fields */
  538.  
  539.         if(extras = xmail_fetchheader_lines(stream, cont_msg, fields)){
  540.         simple_header_parse(extras, fields, values);
  541.         fs_give((void **)&extras);
  542.  
  543.         /* translate RFC 1522 strings */
  544.         for(i = 2; fields[i]; i++)    /* starting with "Lcc" field */
  545.           if(values[i]){
  546.               char *charset;
  547.  
  548.               p = (char *)rfc1522_decode((unsigned char*)tmp_20k_buf,
  549.                          values[i], &charset);
  550.               if(charset)
  551.             fs_give((void **)&charset);
  552.  
  553.               if(p == tmp_20k_buf){
  554.               fs_give((void **)&values[i]);
  555.               values[i] = cpystr(p);
  556.               }
  557.           }
  558.  
  559.         for(pf = *custom, i = (e->newsgroups ? 3 : 4);
  560.             pf && pf->name;
  561.             pf = pf->next, i++){
  562.             if(values[i]){
  563.             if(pf->textbuf)
  564.               fs_give((void **)&pf->textbuf);
  565.  
  566.               pf->textbuf = values[i]; /* freed in pine_send! */
  567.             }
  568.         }
  569.  
  570.         if(values[0])            /* If "Fcc:" was there...  */
  571.           pine_generated = 1;        /* we put it there? */
  572.  
  573.         if(fcc)                /* fcc: special case... */
  574.           *fcc = values[0] ? values[0] : cpystr("");
  575.  
  576.         if(ref_list)
  577.           *ref_list = values[1];
  578.  
  579.         if(lcc)
  580.           *lcc = values[2];
  581.  
  582.         if(!e->newsgroups && values[3])
  583.           e->newsgroups = values[3];
  584.         }
  585.  
  586.         fs_give((void **)&fields);
  587.         fs_give((void **)&values);
  588.  
  589.         *outgoing = copy_envelope(e);
  590.  
  591.         if(!pine_generated){
  592.         /*
  593.          * Now, this is interesting.  We should have found 
  594.          * the "fcc:" field if pine wrote the message being
  595.          * redrafted.  Hence, we probably can't trust the 
  596.          * "originator" type fields, so we'll blast them and let
  597.          * them get set later in pine_send.  This should allow
  598.          * folks with custom or edited From's and such to still
  599.          * use redraft reasonably, without inadvertently sending
  600.          * messages that appear to be "From" others...
  601.          */
  602.         if((*outgoing)->from)
  603.           mail_free_address(&(*outgoing)->from);
  604.  
  605.         /*
  606.          * Ditto for Reply-To and Sender...
  607.          */
  608.         if((*outgoing)->reply_to)
  609.           mail_free_address(&(*outgoing)->reply_to);
  610.  
  611.         if((*outgoing)->sender)
  612.           mail_free_address(&(*outgoing)->sender);
  613.  
  614.         /*
  615.          * Generate a fresh message id for pretty much the same
  616.          * reason From and such got wacked...
  617.          */
  618.         if((*outgoing)->message_id)
  619.           fs_give((void **)&(*outgoing)->message_id);
  620.  
  621.         (*outgoing)->message_id = generate_message_id(ps_global);
  622.         }
  623.  
  624.         if(b && b->type != TYPETEXT){    /* already TYPEMULTIPART */
  625.         *body               = copy_body(NULL, b);
  626.         part               = (*body)->contents.part;
  627.         part->body.type           = TYPETEXT;
  628.         part->body.contents.binary = (void *)so;
  629.         get_body_part_text(stream, &b->contents.part->body,
  630.                    cont_msg, "1", pc, "", NULL);
  631.  
  632.         if(!fetch_contents(stream, cont_msg, *body, *body))
  633.           q_status_message(SM_ORDER | SM_DING, 3, 4,
  634.                    "Error including all message parts");
  635.         }
  636.         else{
  637.         *body             = mail_newbody();
  638.         (*body)->type         = TYPETEXT;
  639.         (*body)->contents.binary = (void *)so;
  640.         get_body_part_text(stream, b, cont_msg, "1", pc, "", NULL);
  641.         }
  642.  
  643.         /* We have what we want, blast this message... */
  644.         if(!mail_elt(stream, cont_msg)->deleted)
  645.           mail_setflag(stream, long2string(cont_msg), "\\DELETED");
  646.  
  647.         redraft_cleanup(c_string, mbox, stream);
  648.         rv = 1;
  649.     }
  650.     else
  651.       rv = 0;
  652.     }
  653.  
  654.     return(rv);
  655. }
  656.  
  657.  
  658.  
  659. /*----------------------------------------------------------------------
  660.     Clear deleted messages from given stream and expunge if necessary
  661.  
  662. Args:    context -- 
  663.     folder --
  664.     stream -- 
  665.  
  666.  ----*/
  667. void
  668. redraft_cleanup(context, folder, stream)
  669.     char       *context;
  670.     char       *folder;
  671.     MAILSTREAM *stream;
  672. {
  673.     if(stream->nmsgs)
  674.       mail_expunge(stream);            /* clean out deleted */
  675.  
  676.     if(stream->nmsgs){                /* close and return */
  677.     if(stream != ps_global->mail_stream)
  678.       mail_close(stream);
  679.     }
  680.     else{                    /* close and delete folder */
  681.     if(stream == ps_global->mail_stream){
  682.         q_status_message1(SM_ORDER, 3, 7,
  683.                  "No more postponed messages, returning to \"%s\"",
  684.                  ps_global->inbox_name);
  685.         do_broach_folder(ps_global->inbox_name, ps_global->context_list);
  686.         ps_global->next_screen = mail_index_screen;
  687.     }
  688.     else
  689.       mail_close(stream);
  690.  
  691.     stream = context_same_stream(context, folder, ps_global->mail_stream);
  692.     if(!stream && ps_global->mail_stream != ps_global->inbox_stream)
  693.       stream = context_same_stream(context, folder,
  694.                        ps_global->inbox_stream);
  695.  
  696.     context_delete(context, stream, folder);
  697.     }
  698. }
  699.  
  700.  
  701.  
  702. /*----------------------------------------------------------------------
  703.     Parse the given header text for any given fields
  704.  
  705. Args:  text -- Text to parse for fcc and attachments refs
  706.        fields -- array of field names to look for
  707.        values -- array of pointer to save values to, returned NULL if
  708.                  fields isn't in text.
  709.  
  710. This function simply looks for the given fields in the given header
  711. text string.
  712. NOTE: newlines are expected CRLF, and we'll ignore continuations
  713.  ----*/
  714. void
  715. simple_header_parse(text, fields, values)
  716.     char   *text, **fields, **values;
  717. {
  718.     int   i, n;
  719.     char *p, *t;
  720.  
  721.     for(i = 0; fields[i]; i++)
  722.       values[i] = NULL;                /* clear values array */
  723.  
  724.     /*---- Loop until the end of the header ----*/
  725.     for(p = text; *p; ){
  726.     for(i = 0; fields[i]; i++)        /* find matching field? */
  727.       if(!struncmp(p, fields[i], (n=strlen(fields[i]))) && p[n] == ':'){
  728.           for(p += n + 1; *p; p++){        /* find start of value */
  729.           if(*p == '\015' && *(p+1) == '\012' && !isspace(*(p+2)))
  730.             break;
  731.  
  732.           if(!isspace(*p))        /* order here is key... */
  733.             break;
  734.           }
  735.  
  736.           if(!values[i]){            /* if we haven't already */
  737.           values[i] = fs_get(strlen(text) + 1);
  738.           values[i][0] = '\0';        /* alloc space for it */
  739.           }
  740.  
  741.           if(*p && *p != '\015'){        /* non-blank value. */
  742.           t = values[i] + (values[i][0] ? strlen(values[i]) : 0);
  743.           while(*p){            /* check for cont'n lines */
  744.               if(*p == '\015' && *(p+1) == '\012'){
  745.               if(isspace(*(p+2))){
  746.                   p += 3;
  747.                   continue;
  748.               }
  749.               else
  750.                 break;
  751.               }
  752.  
  753.               *t++ = *p++;
  754.           }
  755.  
  756.           *t = '\0';
  757.           }
  758.  
  759.           break;
  760.       }
  761.  
  762.         /* Skip to end of line, what ever it was */
  763.         for(; *p ; p++)
  764.       if(*p == '\015' && *(p+1) == '\012'){
  765.           p += 2;
  766.           break;
  767.       }
  768.     }
  769. }
  770.  
  771.  
  772.  
  773. #if defined(DOS) || defined(OS2)
  774. /*----------------------------------------------------------------------
  775.     Verify that the necessary pieces are around to allow for
  776.     message sending under DOS
  777.  
  778. Args:  strict -- tells us if a remote stream is required before
  779.          sending is permitted.
  780.  
  781. The idea is to make sure pine knows enough to put together a valid 
  782. from line.  The things we MUST know are a user-id, user-domain and
  783. smtp server to dump the message off on.  Typically these are 
  784. provided in pine's configuration file, but if not, the user is
  785. queried here.
  786.  ----*/
  787. int
  788. dos_valid_from(strict)
  789.     int strict;
  790. {
  791.     char        prompt[80], answer[60], *p;
  792.     int         rc, i;
  793.     HelpType    help;
  794.  
  795.     /*
  796.      * If we're told to, require that a remote stream exist before
  797.      * permitting mail to get sent.  Someday this will be removed and
  798.      * sent mail sans a stream will get stuffed into an "outbox"...
  799.      */
  800.     if(strict && (!(ps_global->mail_stream
  801.             && !ps_global->mail_stream->anonymous
  802.             && mail_valid_net(ps_global->mail_stream->mailbox,
  803.                       ps_global->mail_stream->dtb,NULL,NULL))
  804.           && !(ps_global->inbox_stream
  805.                && ps_global->inbox_stream != ps_global->mail_stream
  806.                && !ps_global->inbox_stream->anonymous
  807.                && mail_valid_net(ps_global->inbox_stream->mailbox,
  808.                     ps_global->inbox_stream->dtb,NULL,NULL)))){
  809.     q_status_message(SM_ORDER, 3, 7,
  810.              "Can't send message without an open remote folder");
  811.     return(0);
  812.     }
  813.  
  814.     /*
  815.      * query for user name portion of address, use IMAP login
  816.      * name as default
  817.      */
  818.     if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
  819.     if(ps_global->mail_stream
  820.        && ps_global->mail_stream->dtb
  821.        && ps_global->mail_stream->dtb->name
  822.        && !strncmp(ps_global->mail_stream->dtb->name, "imap", 4)
  823.        && (p = imap_user(ps_global->mail_stream)))
  824.       strcpy(answer, p);
  825.     else
  826.       answer[0] = '\0';
  827.  
  828.     sprintf(prompt,"User-id for From address : "); 
  829.  
  830.     help = NO_HELP;
  831.     while(1) {
  832.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  833.                     1,0,prompt,NULL,help,0);
  834.         if(rc == 2)
  835.           continue;
  836.  
  837.         if(rc == 3){
  838.         help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
  839.         continue;
  840.         }
  841.  
  842.         if(rc != 4)
  843.           break;
  844.     }
  845.  
  846.     if(rc == 1 || (rc == 0 && !answer)) {
  847.         q_status_message(SM_ORDER, 3, 4,
  848.            "Send cancelled (User-id must be provided before sending)");
  849.         return(0);
  850.     }
  851.  
  852.     /* save the name */
  853.     sprintf(prompt, "Preserve %s as \"user-id\" in PINERC", answer);
  854.     if(ps_global->blank_user_id
  855.        && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  856.         set_variable(V_USER_ID, answer, 1);
  857.     }
  858.     else{
  859.             fs_give((void **)&(ps_global->VAR_USER_ID));
  860.         ps_global->VAR_USER_ID = cpystr(answer);
  861.     }
  862.     }
  863.  
  864.     /* query for personal name */
  865.     if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'){
  866.     answer[0] = '\0';
  867.     sprintf(prompt,"Personal name for From address : "); 
  868.  
  869.     help = NO_HELP;
  870.     while(1) {
  871.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  872.                     1,0,prompt,NULL,help,0);
  873.         if(rc == 2)
  874.           continue;
  875.  
  876.         if(rc == 3){
  877.         help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
  878.         continue;
  879.         }
  880.  
  881.         if(rc != 4)
  882.           break;
  883.         }
  884.  
  885.     if(rc == 0 && answer){        /* save the name */
  886.         sprintf(prompt, "Preserve %s as \"personal-name\" in PINERC",
  887.             answer);
  888.         if(ps_global->blank_personal_name
  889.            && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  890.         set_variable(V_PERSONAL_NAME, answer, 1);
  891.         }
  892.         else{
  893.             fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
  894.         ps_global->VAR_PERSONAL_NAME = cpystr(answer);
  895.         }
  896.     }
  897.     }
  898.  
  899.     /* 
  900.      * query for host/domain portion of address, using IMAP
  901.      * host as default
  902.      */
  903.     if(ps_global->blank_user_domain 
  904.        || ps_global->maildomain == ps_global->localdomain
  905.        || ps_global->maildomain == ps_global->hostname){
  906.     if(ps_global->inbox_name[0] == '{'){
  907.         for(i=0;ps_global->inbox_name[i+1] != '}'; i++)
  908.         answer[i] = ps_global->inbox_name[i+1];
  909.  
  910.         answer[i] = '\0';
  911.     }
  912.     else
  913.       answer[0] = '\0';
  914.  
  915.     sprintf(prompt,"Host/domain for From address : "); 
  916.  
  917.     help = NO_HELP;
  918.     while(1) {
  919.         rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  920.                     1,0,prompt,NULL,help,0);
  921.         if(rc == 2)
  922.           continue;
  923.  
  924.         if(rc == 3){
  925.         help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
  926.         continue;
  927.         }
  928.  
  929.         if(rc != 4)
  930.           break;
  931.     }
  932.  
  933.     if(rc == 1 || (rc == 0 && !answer)) {
  934.         q_status_message(SM_ORDER, 3, 4,
  935.       "Send cancelled (Host/domain name must be provided before sending)");
  936.         return(0);
  937.     }
  938.  
  939.     /* save the name */
  940.     sprintf(prompt, "Preserve %s as \"user-domain\" in PINERC",
  941.         answer);
  942.     if(!ps_global->userdomain && !ps_global->blank_user_domain
  943.        && want_to(prompt, 'y', 'n', NO_HELP, 0, 0) == 'y'){
  944.         set_variable(V_USER_DOMAIN, answer, 1);
  945.         fs_give((void **)&(ps_global->maildomain));    /* blast old val */
  946.         ps_global->userdomain      = cpystr(answer);
  947.         ps_global->maildomain      = ps_global->userdomain;
  948.     }
  949.     else{
  950.             fs_give((void **)&(ps_global->maildomain));
  951.             ps_global->userdomain = cpystr(answer);
  952.         ps_global->maildomain = ps_global->userdomain;
  953.     }
  954.     }
  955.  
  956.     /* check for smtp server */
  957.     if(!ps_global->VAR_SMTP_SERVER ||
  958.        !ps_global->VAR_SMTP_SERVER[0] ||
  959.        !ps_global->VAR_SMTP_SERVER[0][0]){
  960.     char **list;
  961.  
  962.     if(ps_global->inbox_name[0] == '{'){
  963.         for(i=0;ps_global->inbox_name[i+1] != '}'; i++)
  964.           answer[i] = ps_global->inbox_name[i+1];
  965.         answer[i] = '\0';
  966.     }
  967.     else
  968.           answer[0] = '\0';
  969.  
  970.         sprintf(prompt,"SMTP server to forward message : "); 
  971.  
  972.     help = NO_HELP;
  973.         while(1) {
  974.             rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,79,
  975.                     1,0,prompt,NULL,help,0);
  976.             if(rc == 2)
  977.                   continue;
  978.  
  979.         if(rc == 3){
  980.         help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
  981.         continue;
  982.         }
  983.  
  984.             if(rc != 4)
  985.                   break;
  986.         }
  987.  
  988.         if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
  989.             q_status_message(SM_ORDER, 3, 4,
  990.            "Send cancelled (SMTP server must be provided before sending)");
  991.             return(0);
  992.         }
  993.  
  994.     /* save the name */
  995.         list    = (char **) fs_get(2 * sizeof(char *));
  996.     list[0] = cpystr(answer);
  997.     list[1] = NULL;
  998.     set_variable_list(V_SMTP_SERVER, list);
  999.     fs_give((void *)&list[0]);
  1000.     fs_give((void *)list);
  1001.     }
  1002.  
  1003.     return(1);
  1004. }
  1005. #endif
  1006.  
  1007.  
  1008. /* this is for initializing the fixed header elements in pine_send() */
  1009. /*
  1010. prompt::name::help::prlen::maxlen::realaddr::
  1011. builder::affected_entry::next_affected::selector::key_label::
  1012. display_it::break_on_comma::is_attach::rich_header::only_file_chars::
  1013. single_space::sticky::hd_text
  1014. */
  1015. static struct headerentry he_template[]={
  1016.   {"From    : ",  "From",        h_composer_from,       10, 0, NULL,
  1017.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1018.    0, 1, 0, 1, 0, 1, 0, KS_TOADDRBOOK},
  1019.   {"Reply-To: ",  "Reply To",    h_composer_reply_to,   10, 0, NULL,
  1020.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1021.    0, 1, 0, 1, 0, 1, 0, KS_TOADDRBOOK},
  1022.   {"To      : ",  "To",          h_composer_to,         10, 0, NULL,
  1023.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1024.    0, 1, 0, 0, 0, 1, 0, KS_TOADDRBOOK},
  1025.   {"Cc      : ",  "Cc",          h_composer_cc,         10, 0, NULL,
  1026.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1027.    0, 1, 0, 0, 0, 1, 0, KS_TOADDRBOOK},
  1028.   {"Bcc     : ",  "Bcc",         h_composer_bcc,        10, 0, NULL,
  1029.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1030.    0, 1, 0, 1, 0, 1, 0, KS_TOADDRBOOK},
  1031.   {"Newsgrps: ",  "Newsgroups",  h_composer_news,        10, 0, NULL,
  1032.    news_build,    NULL, NULL, news_group_selector,  "To NwsGrps",
  1033.    0, 1, 0, 1, 0, 1, 0, KS_NONE},
  1034.   {"Fcc     : ",  "Fcc",         h_composer_fcc,        10, 0, NULL,
  1035.    NULL,          NULL, NULL, folders_for_fcc,      "To Fldrs",
  1036.    0, 0, 0, 1, 1, 1, 0, KS_NONE},
  1037.   {"Lcc     : ",  "Lcc",         h_composer_lcc,        10, 0, NULL,
  1038.    build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk",
  1039.    0, 1, 0, 1, 0, 1, 0, KS_NONE},
  1040.   {"Attchmnt: ",  "Attchmnt",    h_composer_attachment, 10, 0, NULL,
  1041.    NULL,          NULL, NULL, NULL,                 "To Files",
  1042.    0, 1, 1, 0, 0, 1, 0, KS_NONE},
  1043.   {"Subject : ",  "Subject",     h_composer_subject,    10, 0, NULL,
  1044.    valid_subject, NULL, NULL, NULL,                 NULL,
  1045.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1046.   {"",            "References",  NO_HELP,               10, 0, NULL,
  1047.    NULL,          NULL, NULL, NULL,                 NULL,
  1048.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1049.   {"",            "Date",        NO_HELP,               10, 0, NULL,
  1050.    NULL,          NULL, NULL, NULL,                 NULL,
  1051.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1052.   {"",            "In-Reply-To", NO_HELP,               10, 0, NULL,
  1053.    NULL,          NULL, NULL, NULL,                 NULL,
  1054.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1055.   {"",            "Message-ID",  NO_HELP,               10, 0, NULL,
  1056.    NULL,          NULL, NULL, NULL,                 NULL,
  1057.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1058.   {"",            "To",          NO_HELP,               10, 0, NULL,
  1059.    NULL,          NULL, NULL, NULL,                 NULL,
  1060.    0, 0, 0, 0, 0, 0, 0, KS_NONE},
  1061. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1062.   {"",            "Sender",      NO_HELP,               10, 0, NULL,
  1063.    NULL,          NULL, NULL, NULL,                 NULL,
  1064.    0, 0, 0, 0, 0, 0, 0, KS_NONE}
  1065. #endif
  1066. };
  1067.  
  1068. static struct headerentry he_custom_addr_templ={
  1069.    NULL,          NULL,          h_composer_custom_addr,10, 0, NULL,
  1070.    build_address, NULL, NULL, addr_book_compose,    "To AddrBk",
  1071.    0, 1, 0, 1, 0, 1, 0, KS_TOADDRBOOK};
  1072. static struct headerentry he_custom_free_templ={
  1073.    NULL,          NULL,          h_composer_custom_free,10, 0, NULL,
  1074.    NULL,          NULL, NULL, NULL,                 NULL,
  1075.    0, 0, 0, 1, 0, 0, 0, KS_NONE};
  1076.  
  1077. /*
  1078.  * Note, these are in the same order in the he_template and pf_template arrays.
  1079.  */
  1080. #define N_FROM    0
  1081. #define N_REPLYTO 1
  1082. #define N_TO      2
  1083. #define N_CC      3
  1084. #define N_BCC     4
  1085. #define N_NEWS    5
  1086. #define N_FCC     6
  1087. #define N_LCC     7
  1088. #define N_ATTCH   8
  1089. #define N_SUBJ    9
  1090. #define N_REF     10
  1091. #define N_DATE    11
  1092. #define N_INREPLY 12
  1093. #define N_MSGID   13
  1094. #define N_NOBODY  14
  1095. #define N_SENDER  15
  1096.  
  1097. /* this is used in pine_send and pine_simple_send */
  1098. /* name::type::canedit::writehdr::localcopy::rcptto */
  1099. static PINEFIELD pf_template[] = {
  1100.   {"From",        Address,    0, 1, 1, 0},
  1101.   {"Reply-To",    Address,    0, 1, 1, 0},
  1102.   {"To",          Address,    1, 1, 1, 1},
  1103.   {"cc",          Address,    1, 1, 1, 1},
  1104.   {"bcc",         Address,    1, 0, 1, 1},
  1105.   {"Newsgroups",  FreeText,    1, 1, 1, 0},
  1106.   {"Fcc",         Fcc,        1, 0, 0, 0},
  1107.   {"Lcc",         Address,    1, 0, 1, 1},
  1108.   {"Attchmnt",    Attachment,    1, 1, 1, 0},
  1109.   {"Subject",     Subject,    1, 1, 1, 0},
  1110.   {"References",  FreeText,    0, 0, 0, 0},
  1111.   {"Date",        FreeText,    0, 1, 1, 0},
  1112.   {"In-Reply-To", FreeText,    0, 1, 1, 0},
  1113.   {"Message-ID",  FreeText,    0, 1, 1, 0},
  1114.   {"To",          FreeText,    0, 0, 0, 0},    /* N_NOBODY */
  1115. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1116.   {"X-Sender",    Address,    0, 1, 1, 0},
  1117. #endif
  1118.   {NULL,         FreeText}
  1119. };
  1120.  
  1121.  
  1122. /*----------------------------------------------------------------------
  1123.      Get addressee for message, then post message
  1124.  
  1125.   Args:  outgoing -- Partially formatted outgoing ENVELOPE
  1126.          body     -- Body of outgoing message
  1127.        opt_prompt -- Optional prompt for optionally_enter call
  1128.     used_tobufval -- The string that the to was eventually set equal to.
  1129.               This gets passed back if non-NULL on entry.
  1130.     prompt_for_to -- Allow user to change recipient
  1131.  
  1132.   Result: message "To: " field is provided and message is sent or cancelled.
  1133.  
  1134.   Fields:
  1135.      remail                -
  1136.      return_path           -
  1137.      date                 added here
  1138.      from                 added here
  1139.      sender                -
  1140.      reply_to              -
  1141.      subject              passed in, NOT edited but maybe canonized here
  1142.      to                   possibly passed in, edited and canonized here
  1143.      cc                    -
  1144.      bcc                   -
  1145.      in_reply_to           -
  1146.      message_id            -
  1147.   
  1148. Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
  1149. with the first part TYPETEXT! All newlines in the text here also end with
  1150. CRLF.
  1151.  
  1152. Returns 0 on success, -1 on failure.
  1153.   ----*/
  1154. int
  1155. pine_simple_send(outgoing, body, opt_prompt, used_tobufval, prompt_for_to)
  1156.     ENVELOPE  *outgoing;  /* envelope for outgoing message */
  1157.     BODY     **body;   
  1158.     char      *opt_prompt;
  1159.     char     **used_tobufval;
  1160.     int        prompt_for_to;
  1161. {
  1162.     char     **tobufp, *p;
  1163.     void      *messagebuf;
  1164.     int        done = 0, retval = 0;
  1165.     int        cnt, i, resize_len, result;
  1166.     PINEFIELD *pfields, *pf, **sending_order;
  1167.     METAENV    header;
  1168.     HelpType   help;
  1169.     ESCKEY_S   ekey[2];
  1170.     BUILDER_ARG ba_fcc;
  1171.  
  1172.     dprint(1, (debugfile,"\n === simple send called === \n"));
  1173.  
  1174.     ba_fcc.tptr            = NULL; ba_fcc.next            = NULL;
  1175.  
  1176.     /* how many fields are there? */
  1177.     cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  1178.  
  1179.     /* temporary PINEFIELD array */
  1180.     i = (cnt + 1) * sizeof(PINEFIELD);
  1181.     pfields = (PINEFIELD *)fs_get((size_t) i);
  1182.     memset(pfields, 0, (size_t) i);
  1183.  
  1184.     i             = (cnt + 1) * sizeof(PINEFIELD *);
  1185.     sending_order = (PINEFIELD **)fs_get((size_t) i);
  1186.     memset(sending_order, 0, (size_t) i);
  1187.  
  1188.     header.env           = outgoing;
  1189.     header.local         = pfields;
  1190.     header.custom        = NULL;
  1191.     header.sending_order = sending_order;
  1192.  
  1193.     /*----- Fill in a few general parts of the envelope ----*/
  1194.     if(!outgoing->date){
  1195.     rfc822_date(tmp_20k_buf);        /* format and copy new date */
  1196.     outgoing->date = cpystr(tmp_20k_buf);
  1197.     }
  1198.  
  1199.     outgoing->from      = generate_from();
  1200.     outgoing->return_path = rfc822_cpy_adr(outgoing->from);
  1201.  
  1202. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1203. #define NN 3
  1204. #else
  1205. #define NN 2
  1206. #endif
  1207.     /* initialize pfield */
  1208.     pf = pfields;
  1209.     for(i=0; i < cnt; i++, pf++){
  1210.  
  1211.         pf->name        = cpystr(pf_template[i].name);
  1212.     if(i == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
  1213.       /* slide string over so it is Sender instead of X-Sender */
  1214.       for(p=pf->name; *(p+1); p++)
  1215.         *p = *(p+2);
  1216.  
  1217.         pf->type        = pf_template[i].type;
  1218.     pf->canedit     = pf_template[i].canedit;
  1219.     pf->rcptto      = pf_template[i].rcptto;
  1220.     pf->writehdr    = pf_template[i].writehdr;
  1221.     pf->localcopy   = pf_template[i].localcopy;
  1222.         pf->he          = NULL; /* unused */
  1223.         pf->next        = pf + 1;
  1224.  
  1225.         switch(pf->type){
  1226.           case FreeText:
  1227.             switch(i){
  1228.               case N_NEWS:
  1229.         pf->text        = &outgoing->newsgroups;
  1230.         sending_order[0]    = pf;
  1231.                 break;
  1232.  
  1233.               case N_DATE:
  1234.         pf->text        = &outgoing->date;
  1235.         sending_order[1]    = pf;
  1236.                 break;
  1237.  
  1238.               case N_INREPLY:
  1239.         pf->text        = &outgoing->in_reply_to;
  1240.         sending_order[NN+9]    = pf;
  1241.                 break;
  1242.  
  1243.               case N_MSGID:
  1244.         pf->text        = &outgoing->message_id;
  1245.         sending_order[NN+10]    = pf;
  1246.                 break;
  1247.  
  1248.               case N_NOBODY: /* won't be used here */
  1249.         sending_order[NN+5]    = pf;
  1250.                 break;
  1251.  
  1252.               case N_REF: /* won't be used here */
  1253.         sending_order[NN+11]    = pf;
  1254.                 break;
  1255.  
  1256.               default:
  1257.                 q_status_message1(SM_ORDER,3,3,
  1258.             "Internal error: 1)FreeText header %d", (void *)i);
  1259.                 break;
  1260.             }
  1261.  
  1262.             break;
  1263.  
  1264.           case Attachment:
  1265.             break;
  1266.  
  1267.           case Address:
  1268.             switch(i){
  1269.           case N_FROM:
  1270.         sending_order[2]    = pf;
  1271.         pf->addr        = &outgoing->from;
  1272.         break;
  1273.  
  1274.           case N_TO:
  1275.         sending_order[NN+2]    = pf;
  1276.         pf->addr        = &outgoing->to;
  1277.             tobufp            = &pf->scratch;
  1278.         break;
  1279.  
  1280.           case N_CC:
  1281.         sending_order[NN+3]    = pf;
  1282.         pf->addr        = &outgoing->cc;
  1283.         break;
  1284.  
  1285.           case N_BCC:
  1286.         sending_order[NN+4]    = pf;
  1287.         pf->addr        = &outgoing->bcc;
  1288.         break;
  1289.  
  1290.           case N_REPLYTO:
  1291.         sending_order[NN+1]    = pf;
  1292.         pf->addr        = &outgoing->reply_to;
  1293.         break;
  1294.  
  1295.           case N_LCC:        /* won't be used here */
  1296.         sending_order[NN+7]    = pf;
  1297.         break;
  1298.  
  1299. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1300.               case N_SENDER:
  1301.         sending_order[3]    = pf;
  1302.         pf->addr        = &outgoing->sender;
  1303.                 break;
  1304. #endif
  1305.  
  1306.               default:
  1307.                 q_status_message1(SM_ORDER,3,3,
  1308.             "Internal error: Address header %d", (void *) i);
  1309.                 break;
  1310.             }
  1311.             break;
  1312.  
  1313.           case Fcc:
  1314.         sending_order[NN+8] = pf;
  1315.         ba_fcc.tptr            = NULL;
  1316.         ba_fcc.next            = NULL;
  1317.         pf->text        = &ba_fcc.tptr;
  1318.             break;
  1319.  
  1320.       case Subject:
  1321.         sending_order[NN+6]    = pf;
  1322.         pf->text        = &outgoing->subject;
  1323.         break;
  1324.  
  1325.           default:
  1326.             q_status_message1(SM_ORDER,3,3,
  1327.         "Unknown header type %d in pine_simple_send", (void *)pf->type);
  1328.             break;
  1329.         }
  1330.     }
  1331.  
  1332.     pf->next = NULL;
  1333.  
  1334.     ekey[0].ch    = ctrl('T');
  1335.     ekey[0].rval  = 2;
  1336.     ekey[0].name  = "^T";
  1337.     ekey[0].label = "To AddrBk";
  1338.     ekey[1].ch    = -1;
  1339.  
  1340.     /*----------------------------------------------------------------------
  1341.        Loop editing the "To: " field until everything goes well
  1342.      ----*/
  1343.     help = NO_HELP;
  1344.  
  1345.     while(!done){
  1346.     outgoing2strings(&header, *body, &messagebuf, NULL);
  1347.  
  1348.     resize_len = max(MAXPATH, strlen(*tobufp));
  1349.     fs_resize((void **)tobufp, resize_len+1);
  1350.  
  1351.     if(prompt_for_to)
  1352.       i = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
  1353.                 0, resize_len, 1, 0,
  1354.                 opt_prompt
  1355.                   ? opt_prompt
  1356.                   : outgoing->remail == NULL 
  1357.                     ? "FORWARD (as e-mail) to : "
  1358.                     : "BOUNCE (redirect) message to : ",
  1359.                 ekey, help, 0);
  1360.     else
  1361.       i = 0;
  1362.  
  1363.     switch(i){
  1364.       case -1:
  1365.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  1366.                  "Internal problem encountered");
  1367.         retval = -1;
  1368.         done++;
  1369.         break;
  1370.  
  1371.       case 0:
  1372.         if(**tobufp != '\0'){
  1373.         char *errbuf, *addr, prompt[200];
  1374.         int   tolen, ch;
  1375.  
  1376.         errbuf = NULL;
  1377.         ba_fcc.tptr = NULL;
  1378.         ba_fcc.next = NULL;
  1379.         if(build_address(*tobufp, &addr, &errbuf, &ba_fcc) >= 0){
  1380.             if(errbuf)
  1381.               fs_give((void **)&errbuf);
  1382.  
  1383.             if(strlen(*tobufp) < (tolen = strlen(addr) + 1))
  1384.               fs_resize((void **)tobufp, (size_t) tolen);
  1385.  
  1386.             strcpy(*tobufp, addr);
  1387.             if(used_tobufval)
  1388.               *used_tobufval = cpystr(addr);
  1389.  
  1390.             sprintf(prompt, "Send message to \"%.50s\"", addr);
  1391.             strings2outgoing(&header, body, NULL);
  1392.             if(addr)
  1393.               fs_give((void **)&addr);
  1394.  
  1395.             if(prompt_for_to && check_addresses(&header))
  1396.               /*--- Addresses didn't check out---*/
  1397.               continue;
  1398.  
  1399.             /* confirm address */
  1400.             if(!prompt_for_to
  1401.              || (ch = want_to(prompt, 'y', 'n', NO_HELP, 0, 0)) == 'y'){
  1402.             char *fcc = NULL;
  1403.             CONTEXT_S *fcc_cntxt = NULL;
  1404.  
  1405.             if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
  1406.                 fcc = ba_fcc.tptr;
  1407.                 set_last_fcc(fcc);
  1408.             }
  1409.  
  1410.             /*---- Check out fcc -----*/
  1411.             if(fcc && *fcc){
  1412.                 local_written = 0;
  1413.                 if(!open_fcc(fcc, &fcc_cntxt, 0)
  1414.            || !(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS))){
  1415.                 /* Open or allocation of fcc failed */
  1416.                 q_status_message(SM_ORDER, 3, 5,
  1417.                      "Message NOT sent or written to fcc.");
  1418.                 dprint(4, (debugfile,"can't open fcc, cont\n"));
  1419.                 if(!prompt_for_to){
  1420.                     retval = -1;
  1421.                     goto finish;
  1422.                 }
  1423.                 else
  1424.                   continue;
  1425.                 }
  1426.             }
  1427.             else
  1428.               local_so = NULL;
  1429.  
  1430.             result = call_mailer(&header, *body);
  1431.             /*----- Was there an fcc involved? -----*/
  1432.             if(local_so){
  1433.                 if(result == 1
  1434.                    || (result == 0
  1435.                && pine_rfc822_output(&header, *body, NULL, NULL))){
  1436.                 char label[50];
  1437.  
  1438.                 strcpy(label, "Fcc");
  1439.                 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC))
  1440.                   sprintf(label + 3, " to %.40s", fcc);
  1441.  
  1442.                 /* Now actually copy to fcc folder and close */
  1443.                 write_fcc(fcc, fcc_cntxt, local_so, label);
  1444.                 }
  1445.                 else if(result == 0){
  1446.                 q_status_message(SM_ORDER,3,5,
  1447.                     "Fcc Failed!.  No message saved.");
  1448.                 retval = -1;
  1449.                 dprint(1,
  1450.                   (debugfile, "explicit fcc write failed!\n"));
  1451.                 }
  1452.  
  1453.                 so_give(&local_so);
  1454.             }
  1455.  
  1456.             if(result < 0){
  1457.                 dprint(1, (debugfile, "Bounce failed\n"));
  1458.                 if(!prompt_for_to)
  1459.                   retval = -1;
  1460.                 else
  1461.                   continue;
  1462.             }
  1463.             }
  1464.             else{
  1465.             q_status_message(SM_ORDER, 0, 3, "Send cancelled");
  1466.             retval = -1;
  1467.             }
  1468.         }
  1469.         else{
  1470.             q_status_message1(SM_ORDER | SM_DING, 3, 5,
  1471.                       "Error in address: %s", errbuf);
  1472.             if(errbuf)
  1473.               fs_give((void **)&errbuf);
  1474.  
  1475.             if(!prompt_for_to)
  1476.               retval = -1;
  1477.             else
  1478.               continue;
  1479.         }
  1480.  
  1481.         }
  1482.         else{
  1483.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  1484.                  "No addressee!  No e-mail sent.");
  1485.         retval = -1;
  1486.         }
  1487.  
  1488.         done++;
  1489.         break;
  1490.  
  1491.       case 1:
  1492.         q_status_message(SM_ORDER, 0, 3, "Send cancelled");
  1493.         done++;
  1494.         retval = -1;
  1495.         break;
  1496.  
  1497.       case 2: /* ^T */
  1498.         {
  1499.         void (*redraw) () = ps_global->redrawer;
  1500.         char  *returned_addr = NULL;
  1501.         int    len;
  1502.  
  1503.         push_titlebar_state();
  1504.         returned_addr = addr_book_bounce();
  1505.         if(returned_addr){
  1506.         if(resize_len < (len = strlen(returned_addr) + 1))
  1507.           fs_resize((void **)tobufp, (size_t)len);
  1508.  
  1509.         strcpy(*tobufp, returned_addr);
  1510.         fs_give((void **)&returned_addr);
  1511.         strings2outgoing(&header, body, NULL);
  1512.         }
  1513.  
  1514.         ClearScreen();
  1515.         pop_titlebar_state();
  1516.         redraw_titlebar();
  1517.         if(ps_global->redrawer = redraw) /* reset old value, and test */
  1518.           (*ps_global->redrawer)();
  1519.         }
  1520.  
  1521.         break;
  1522.  
  1523.       case 3:
  1524.         help = (help == NO_HELP)
  1525.                 ? (outgoing->remail == NULL
  1526.                 ? h_anon_forward
  1527.                 : h_bounce)
  1528.                 : NO_HELP;
  1529.         break;
  1530.  
  1531.       case 4:                /* can't suspend */
  1532.       default:
  1533.         break;
  1534.     }
  1535.     }
  1536.  
  1537. finish:
  1538.     if(ba_fcc.tptr)
  1539.       fs_give((void **)&ba_fcc.tptr);
  1540.  
  1541.     for(i=0; i < cnt; i++)
  1542.       fs_give((void **)&pfields[i].name);
  1543.  
  1544.     fs_give((void **)&pfields);
  1545.     fs_give((void **)&sending_order);
  1546.  
  1547.     return(retval);
  1548. }
  1549.  
  1550.  
  1551. /*----------------------------------------------------------------------
  1552.      Prepare data structures for pico, call pico, then post message
  1553.  
  1554.   Args:  outgoing     -- Partially formatted outgoing ENVELOPE
  1555.          body         -- Body of outgoing message
  1556.          editor_title -- Title for anchor line in composer
  1557.          fcc          -- The file carbon copy field
  1558.      reply_list   -- List of raw c-client message numbers of
  1559.              we're replying to.  This should be GIDs
  1560.              after IMAP4.
  1561.      ref_list     -- News references list of message id's
  1562.      custom -- custom header list. Passed void * so routines outside
  1563.            send.c don't need to know what PINEFIELD is...
  1564.  
  1565.   Result: message is edited, then postponed, cancelled or sent.
  1566.  
  1567.   Fields:
  1568.      remail                -
  1569.      return_path           -
  1570.      date                 added here
  1571.      from                 added here
  1572.      sender                -
  1573.      reply_to              -
  1574.      subject              passed in, edited and cannonized here
  1575.      to                   possibly passed in, edited and cannonized here
  1576.      cc                   possibly passed in, edited and cannonized here
  1577.      bcc                  edited and cannonized here
  1578.      in_reply_to          generated in reply() and passed in
  1579.      message_id            -
  1580.   
  1581.  Storage for these fields comes from anywhere outside. It is remalloced
  1582.  here so the composer can realloc them if needed. The copies here are also 
  1583.  freed here.
  1584.  
  1585. Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
  1586. with the first part TYPETEXT! All newlines in the text here also end with
  1587. CRLF.
  1588.  
  1589. There's a further assumption that the text in the TYPETEXT part is 
  1590. stored in a storage object (see filter.c).
  1591.   ----*/
  1592. void
  1593. pine_send(outgoing, body, editor_title, fcc_arg, reply_list, ref_list,
  1594.       reply_prefix, lcc_arg, custom, sticky_fcc)
  1595.     ENVELOPE  *outgoing;  /* c-client envelope for outgoing message */
  1596.     BODY     **body;   
  1597.     char      *editor_title;
  1598.     char      *fcc_arg;
  1599.     long      *reply_list;
  1600.     char      *ref_list;
  1601.     char      *reply_prefix;
  1602.     char      *lcc_arg;
  1603.     void      *custom;
  1604.     int        sticky_fcc;
  1605. {
  1606.     int            i, fixed_cnt, total_cnt, index,
  1607.             editor_result = 0, body_start = 0;
  1608.     char           *p, *addr, *fcc, *fcc_to_free = NULL;
  1609.     struct headerentry *he, *headents, *he_to, *he_fcc, *he_news, *he_lcc;
  1610.     PINEFIELD          *pfields, *pf, *pf_nobody, *pf_ref, *pf_fcc,
  1611.               **sending_order;
  1612.     METAENV             header;
  1613.     ADDRESS            *lcc_addr = NULL;
  1614.     STORE_S           *orig_so = NULL;
  1615. #ifdef    DOS
  1616.     char               *reserve;
  1617. #endif
  1618.  
  1619.     dprint(1, (debugfile,"\n=== send called ===\n"));
  1620.  
  1621.     /*
  1622.      * Cancel any pending initial commands since pico uses a different
  1623.      * input routine.  If we didn't cancel them, they would happen after
  1624.      * we returned from the editor, which would be confusing.
  1625.      */
  1626.     if(ps_global->in_init_seq){
  1627.     ps_global->in_init_seq = 0;
  1628.     ps_global->save_in_init_seq = 0;
  1629.     clear_cursor_pos();
  1630.     if(ps_global->initial_cmds){
  1631.         if(ps_global->free_initial_cmds)
  1632.           fs_give((void **)&(ps_global->free_initial_cmds));
  1633.  
  1634.         ps_global->initial_cmds = 0;
  1635.     }
  1636.  
  1637.     F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
  1638.     }
  1639.  
  1640. #if defined(DOS) || defined(OS2)
  1641.     if(!dos_valid_from(1))
  1642.       return;
  1643.  
  1644.     pbuf.upload           = NULL;
  1645. #else
  1646.     pbuf.upload           = (ps_global->VAR_UPLOAD_CMD
  1647.               && ps_global->VAR_UPLOAD_CMD[0])
  1648.               ? upload_msg_to_pico : NULL;
  1649. #endif
  1650.  
  1651.     pbuf.raw_io        = Raw;
  1652.     pbuf.showmsg       = display_message_for_pico;
  1653.     pbuf.newmail       = new_mail_for_pico;
  1654.     pbuf.msgntext      = message_format_for_pico;
  1655.     pbuf.ckptdir       = checkpoint_dir_for_pico;
  1656.     pbuf.mimetype      = mime_type_for_pico;
  1657.     pbuf.exittest      = send_exit_for_pico;
  1658.     pbuf.resize           = resize_for_pico;
  1659.     pbuf.keybinit      = init_keyboard;
  1660.     pbuf.helper        = helper;
  1661.     pbuf.alt_ed        = (F_ON(F_ENABLE_ALT_ED,ps_global) ||
  1662.               F_ON(F_ALT_ED_NOW,ps_global))      ?
  1663.                 ps_global->VAR_EDITOR : NULL;
  1664.     pbuf.alt_spell     = ps_global->VAR_SPELLER;
  1665.     pbuf.quote_str     = reply_prefix;
  1666.     pbuf.fillcolumn    = ps_global->composer_fillcol;
  1667.     pbuf.menu_rows     = FOOTER_ROWS(ps_global) - 1;
  1668.     pbuf.ins_help      = h_composer_ins;
  1669.     pbuf.search_help   = h_composer_search;
  1670.     pbuf.browse_help   = h_composer_browse;
  1671.     pbuf.composer_help = h_composer;
  1672.     pbuf.pine_anchor   = set_titlebar(editor_title, ps_global->mail_stream,
  1673.                       ps_global->context_current,
  1674.                       ps_global->cur_folder,ps_global->msgmap, 
  1675.                       0, FolderName, 0, 0);
  1676.     pbuf.pine_version  = pine_version;
  1677.     pbuf.pine_flags    = flags_for_pico(ps_global);
  1678.     if(ps_global->VAR_OPER_DIR){
  1679.     pbuf.oper_dir    = ps_global->VAR_OPER_DIR;
  1680.     pbuf.pine_flags |= P_TREE;
  1681.     }
  1682.  
  1683.     /* NOTE: initial cursor position set below */
  1684.  
  1685.     dprint(9, (debugfile, "flags: %x\n", pbuf.pine_flags));
  1686.  
  1687.     /*
  1688.      * When user runs compose and the current folder is a newsgroup,
  1689.      * offer to post to the current newsgroup.
  1690.      */
  1691.     if(!outgoing->to && !(outgoing->newsgroups && *outgoing->newsgroups)
  1692.        && ps_global->mail_stream && ps_global->mail_stream->mailbox
  1693.        && ps_global->mail_stream->mailbox[0] == '*'){
  1694.     char  prompt[200];
  1695.     char  newsgroup_name[MAILTMPLEN];
  1696.  
  1697.     newsgroup_name[0] = '\0';
  1698.  
  1699.     /* if remote, c-client has a function to parse out mailbox */
  1700.     if(ps_global->mail_stream->mailbox[1] == '{')
  1701.       mail_valid_net(ps_global->mail_stream->mailbox,
  1702.         ps_global->mail_stream->dtb,
  1703.         NULL,
  1704.         newsgroup_name);
  1705.     else
  1706.       (void)strcpy(newsgroup_name, &(ps_global->mail_stream->mailbox[1]));
  1707.  
  1708.     /*
  1709.      * Replies don't get this far because To or Newsgroups will already
  1710.      * be filled in.  So must be either ordinary compose or forward.
  1711.      * Forward sets subject, so use that to tell the difference.
  1712.      */
  1713.     if(newsgroup_name[0] && !outgoing->subject){
  1714.         int ch = 'y';
  1715.         int ret_val;
  1716.         char *errmsg = NULL;
  1717.         BUILDER_ARG     *fcc_build = NULL;
  1718.  
  1719.         if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
  1720.         sprintf(prompt,
  1721.             "Post to current newsgroup (%s)", newsgroup_name);
  1722.         ch = want_to(prompt, 'y', 'x', NO_HELP, 0, 0);
  1723.         }
  1724.  
  1725.         switch(ch){
  1726.           case 'y':
  1727.         if(outgoing->newsgroups)
  1728.           fs_give((void **)&outgoing->newsgroups); 
  1729.  
  1730.         if(!fcc_arg){
  1731.             fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
  1732.             fcc_build->tptr = fcc_to_free;
  1733.             fcc_build->next = NULL;
  1734.         }
  1735.  
  1736.         ret_val = news_build(newsgroup_name, &outgoing->newsgroups,
  1737.                      &errmsg, fcc_build);
  1738.  
  1739.         if(ret_val == -1){
  1740.             if(outgoing->newsgroups)
  1741.               fs_give((void **)&outgoing->newsgroups); 
  1742.  
  1743.             outgoing->newsgroups = cpystr(newsgroup_name);
  1744.         }
  1745.  
  1746.         if(!fcc_arg){
  1747.             fcc_arg = fcc_to_free = fcc_build->tptr;
  1748.             fs_give((void **)&fcc_build);
  1749.         }
  1750.  
  1751.         if(errmsg){
  1752.             if(*errmsg){
  1753.             q_status_message(SM_ORDER, 3, 3, errmsg);
  1754.             display_message(NO_OP_COMMAND);
  1755.             }
  1756.  
  1757.             fs_give((void **)&errmsg);
  1758.         }
  1759.  
  1760.         break;
  1761.  
  1762.           case 'x': /* ^C */
  1763.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  1764.         dprint(4, (debugfile, "=== send: cancelled\n"));
  1765.         return;
  1766.  
  1767.           case 'n':
  1768.         break;
  1769.  
  1770.           default:
  1771.         break;
  1772.         }
  1773.     }
  1774.     }
  1775.  
  1776.     /* how many fixed fields are there? */
  1777.     fixed_cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  1778.  
  1779.     total_cnt = fixed_cnt + count_custom_hdrs();
  1780.  
  1781.     /* the fixed part of the PINEFIELDs */
  1782.     i       = fixed_cnt * sizeof(PINEFIELD);
  1783.     pfields = (PINEFIELD *)fs_get((size_t) i);
  1784.     memset(pfields, 0, (size_t) i);
  1785.  
  1786.     /* temporary headerentry array for pico */
  1787.     i        = (total_cnt + 1) * sizeof(struct headerentry);
  1788.     headents = (struct headerentry *)fs_get((size_t) i);
  1789.     memset(headents, 0, (size_t) i);
  1790.  
  1791.     i             = total_cnt * sizeof(PINEFIELD *);
  1792.     sending_order = (PINEFIELD **)fs_get((size_t) i);
  1793.     memset(sending_order, 0, (size_t) i);
  1794.  
  1795.     pbuf.headents        = headents;
  1796.     header.env           = outgoing;
  1797.     header.local         = pfields;
  1798.     header.sending_order = sending_order;
  1799.  
  1800.     /* custom part of PINEFIELDs */
  1801.     header.custom = (custom) ? (PINEFIELD *)custom : get_dflt_custom_hdrs();
  1802.  
  1803.     he = headents;
  1804.     pf = pfields;
  1805.  
  1806.     /*
  1807.      * For Address types, pf->addr points to an ADDRESS *.
  1808.      * If that address is in the "outgoing" envelope, it will
  1809.      * be freed by the caller, otherwise, it should be freed here.
  1810.      * Pf->textbuf for an Address is used a little to set up a default,
  1811.      * but then is freed right away below.  Pf->scratch is used for a
  1812.      * pointer to some alloced space for pico to edit in.  Addresses in
  1813.      * the custom area are freed by free_customs().
  1814.      *
  1815.      * For FreeText types, pf->addr is not used.  Pf->text points to a
  1816.      * pointer that points to the text.  Pf->textbuf points to a copy of
  1817.      * the text that must be freed before we leave, otherwise, it is
  1818.      * probably a pointer into the envelope and that gets freed by the
  1819.      * caller.
  1820.      *
  1821.      * He->realaddr is the pointer to the text that pico actually edits.
  1822.      */
  1823.  
  1824. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1825. #define NN 3
  1826. #else
  1827. #define NN 2
  1828. #endif
  1829. #define START_OF_CUSTOM_SENDING_ORDER (NN+12)
  1830.  
  1831.     /* initialize the fixed header elements of the two temp arrays */
  1832.     for(i=0; i < fixed_cnt; i++, pf++){
  1833.  
  1834.     /* slightly different editing order if sending to news */
  1835.     if(outgoing->newsgroups && *outgoing->newsgroups){
  1836.         index = (i == 0) ? N_FROM :
  1837.              (i == 1) ? N_REPLYTO :
  1838.               (i == 2) ? N_NEWS :
  1839.                (i == 3) ? N_TO :
  1840.             (i == 4) ? N_CC :
  1841.              (i == 5) ? N_BCC :
  1842.               (i == 6) ? N_FCC :
  1843.                (i == 7) ? N_LCC :
  1844.                 (i == 8) ? N_ATTCH :
  1845.                  (i == 9) ? N_SUBJ :
  1846.                   (i == 10) ? N_REF :
  1847.                    (i == 11) ? N_DATE :
  1848.                 (i == 12) ? N_INREPLY :
  1849.                  (i == 13) ? N_MSGID :
  1850.                   (i == 14) ? N_NOBODY :
  1851. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  1852.                    (i == 15) ? N_SENDER :
  1853. #endif
  1854.                               i;
  1855.     }
  1856.     else
  1857.       index = i;
  1858.  
  1859.     if(i > 15)
  1860.       q_status_message1(SM_ORDER,3,7,
  1861.           "Internal error: i=%d in pine_send", (void *)i);
  1862.  
  1863.     /* copy the templates */
  1864.     *he             = he_template[index];
  1865.  
  1866.     pf->name        = cpystr(pf_template[index].name);
  1867.     if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
  1868.       /* slide string over so it is Sender instead of X-Sender */
  1869.       for(p=pf->name; *(p+1); p++)
  1870.         *p = *(p+2);
  1871.  
  1872.     pf->type        = pf_template[index].type;
  1873.     pf->canedit     = pf_template[index].canedit;
  1874.     pf->rcptto      = pf_template[index].rcptto;
  1875.     pf->writehdr    = pf_template[index].writehdr;
  1876.     pf->localcopy   = pf_template[index].localcopy;
  1877.     pf->he          = he;
  1878.     pf->next        = pf + 1;
  1879.  
  1880.     he->rich_header = view_as_rich(pf->name, he->rich_header);
  1881.  
  1882.     switch(pf->type){
  1883.       case FreeText:           /* realaddr points to c-client env */
  1884.         if(index == N_NEWS){
  1885.         sending_order[0]    = pf;
  1886.         he->realaddr        = &outgoing->newsgroups;
  1887.         /* If there is a newsgrp already, we'd better show them */
  1888.         if(outgoing->newsgroups && *outgoing->newsgroups)
  1889.           he->rich_header = 0; /* force on by default */
  1890.  
  1891.             he_news            = he;
  1892.  
  1893.         /*
  1894.          * If this field doesn't already have a value, then we want
  1895.          * to check for a default value assigned by the user.  If no
  1896.          * default, give it an alloced empty string.
  1897.          */
  1898.         if(!*he->realaddr){        /* no value */
  1899.             set_default_hdrval(pf);    /* default in pf->textbuf */
  1900.             *he->realaddr = pf->textbuf;
  1901.             pf->textbuf   = NULL;
  1902.         }
  1903.  
  1904.         pf->text = he->realaddr;
  1905.         }
  1906.         else if(index == N_DATE){
  1907.         sending_order[1]    = pf;
  1908.         pf->text        = &outgoing->date;
  1909.         pf->he            = NULL;
  1910.         }
  1911.         else if(index == N_INREPLY){
  1912.         sending_order[NN+9]    = pf;
  1913.         pf->text        = &outgoing->in_reply_to;
  1914.         pf->he            = NULL;
  1915.         }
  1916.         else if(index == N_MSGID){
  1917.         sending_order[NN+10]    = pf;
  1918.         pf->text        = &outgoing->message_id;
  1919.         pf->he            = NULL;
  1920.         }
  1921.         else if(index == N_NOBODY){
  1922.         static char *undisclosed_msg = "undisclosed recipients: ;";
  1923.  
  1924.         sending_order[NN+5]    = pf;
  1925.         pf_nobody        = pf;
  1926.         pf->text        = &undisclosed_msg;
  1927.         pf->he            = NULL;
  1928.         }
  1929.         else if(index == N_REF){
  1930.         sending_order[NN+11]    = pf;
  1931.         pf_ref            = pf;
  1932.         if(ref_list)
  1933.           pf->textbuf = cpystr(ref_list);
  1934.  
  1935.         pf->text        = &pf->textbuf;
  1936.         pf->he            = NULL;
  1937.         break;
  1938.         }
  1939.         else{
  1940.         q_status_message(SM_ORDER | SM_DING, 3, 7,
  1941.                 "Botched: Unmatched FreeText header in pine_send");
  1942.         break;
  1943.         }
  1944.  
  1945.         break;
  1946.  
  1947.       /* can't do a default for this one */
  1948.       case Attachment:
  1949.         /* If there is an attachment already, we'd better show them */
  1950.             if(body && *body && (*body)->type != TYPETEXT)
  1951.           he->rich_header = 0; /* force on by default */
  1952.  
  1953.         break;
  1954.  
  1955.       case Address:
  1956.         switch(index){
  1957.           case N_FROM:
  1958.         sending_order[2]    = pf;
  1959.         pf->addr        = &outgoing->from;
  1960.         break;
  1961.  
  1962.           case N_TO:
  1963.         sending_order[NN+2]    = pf;
  1964.         pf->addr        = &outgoing->to;
  1965.         /* If already set, make it act like we typed it in */
  1966.         if(outgoing->to
  1967.            && outgoing->to->mailbox
  1968.            && outgoing->to->mailbox[0])
  1969.           he->sticky = 1;
  1970.  
  1971.         he_to            = he;
  1972.         break;
  1973.  
  1974.           case N_CC:
  1975.         sending_order[NN+3]    = pf;
  1976.         pf->addr        = &outgoing->cc;
  1977.         break;
  1978.  
  1979.           case N_BCC:
  1980.         sending_order[NN+4]    = pf;
  1981.         pf->addr        = &outgoing->bcc;
  1982.         /* if bcc exists, make sure it's exposed so nothing's
  1983.          * sent by mistake...
  1984.          */
  1985.         if(outgoing->bcc)
  1986.           he->display_it = 1;
  1987.  
  1988.         break;
  1989.  
  1990.           case N_REPLYTO:
  1991.         sending_order[NN+1]    = pf;
  1992.         pf->addr        = &outgoing->reply_to;
  1993.         break;
  1994.  
  1995.           case N_LCC:
  1996.         sending_order[NN+7]    = pf;
  1997.         pf->addr        = &lcc_addr;
  1998.         he_lcc            = he;
  1999.         if(lcc_arg){
  2000.             build_address(lcc_arg, &addr, NULL, NULL);
  2001.             rfc822_parse_adrlist(&lcc_addr, addr,
  2002.             ps_global->maildomain);
  2003.             fs_give((void **)&addr);
  2004.         }
  2005.  
  2006.         break;
  2007.  
  2008. #if    !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
  2009.               case N_SENDER:
  2010.         sending_order[3]    = pf;
  2011.         pf->addr        = &outgoing->sender;
  2012.                 break;
  2013. #endif
  2014.  
  2015.           default:
  2016.         q_status_message1(SM_ORDER,3,7,
  2017.             "Internal error: Address header %d", (void *)index);
  2018.         break;
  2019.         }
  2020.  
  2021.         /*
  2022.          * If this is a reply to news, don't show the regular email
  2023.          * recipient headers (unless they are non-empty).
  2024.          */
  2025.         if((outgoing->newsgroups && *outgoing->newsgroups)
  2026.            && (index == N_TO || index == N_CC
  2027.            || index == N_BCC || index == N_LCC)
  2028.            && (!*pf->addr)){
  2029.         he->rich_header = 1; /* hide */
  2030.  
  2031.         /*
  2032.          * If this address doesn't already have a value, then we check
  2033.          * for a default value assigned by the user.
  2034.          */
  2035.         }
  2036.         else if(!*pf->addr
  2037.             || !(*pf->addr)->mailbox
  2038.             || !(*pf->addr)->mailbox[0]){
  2039.         set_default_hdrval(pf);
  2040.  
  2041. #ifndef ALLOW_CHANGING_FROM
  2042.         if(index != N_FROM){  /* don't set default for From */
  2043. #endif
  2044.             if(pf->textbuf && *pf->textbuf){
  2045.                 /* strip quotes around whole default */
  2046.                 if(*pf->textbuf == '"'){
  2047.                   char *p;
  2048.  
  2049.                   /* strip trailing whitespace */
  2050.                   p = pf->textbuf + strlen(pf->textbuf) - 1;
  2051.                   while(isspace(*p) && p > pf->textbuf)
  2052.                 p--;
  2053.  
  2054.                   /* a set of surrounding quotes we want to strip */
  2055.                   if(*p == '"'){
  2056.                 *p = '\0';
  2057.                 for(p=pf->textbuf; *(p+1); p++)
  2058.                   *p = *(p+1);
  2059.  
  2060.                 *p = '\0';
  2061.                   }
  2062.                 }
  2063.  
  2064.                 build_address(pf->textbuf, &addr, NULL, NULL);
  2065.                 rfc822_parse_adrlist(pf->addr,
  2066.                 addr, ps_global->maildomain);
  2067.                 fs_give((void **)&addr);
  2068.             }
  2069. #ifndef ALLOW_CHANGING_FROM
  2070.         }
  2071. #endif
  2072.  
  2073.         /* if we still don't have a from */
  2074.         if(index == N_FROM && !*pf->addr)
  2075.           *pf->addr = generate_from();
  2076.  
  2077.         }else if(index == N_FROM || index == N_REPLYTO){
  2078.         /* side effect of this may set canedit */
  2079.         set_default_hdrval(pf);
  2080.         }
  2081.  
  2082.         if(!outgoing->return_path)
  2083.           outgoing->return_path = rfc822_cpy_adr(outgoing->from);
  2084.  
  2085.         if(pf->textbuf)        /* free default value in any case */
  2086.           fs_give((void **)&pf->textbuf);
  2087.  
  2088.         /* outgoing2strings will alloc the string pf->scratch below */
  2089.         he->realaddr = &pf->scratch;
  2090.         break;
  2091.  
  2092.       case Fcc:
  2093.         sending_order[NN+8] = pf;
  2094.         pf_fcc              = pf;
  2095.         fcc                 = get_fcc(fcc_arg);
  2096.         if(fcc_to_free){
  2097.         fs_give((void **)&fcc_to_free);
  2098.         fcc_arg = NULL;
  2099.         }
  2100.  
  2101.         if(sticky_fcc)
  2102.           he->sticky = 1;
  2103.  
  2104.         if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
  2105.           he->display_it = 1;  /* start this off showing */
  2106.  
  2107.         he->realaddr  = &fcc;
  2108.         pf->text      = &fcc;
  2109.         he_fcc        = he;
  2110.         break;
  2111.  
  2112.       case Subject :
  2113.         sending_order[NN+6]    = pf;
  2114.         if(outgoing->subject && *outgoing->subject){
  2115.         pf->scratch = cpystr(outgoing->subject);
  2116.         }
  2117.         else{
  2118.         set_default_hdrval(pf);    /* default in pf->textbuf */
  2119.         pf->scratch = pf->textbuf;
  2120.         pf->textbuf   = NULL;
  2121.         }
  2122.  
  2123.         he->realaddr = &pf->scratch;
  2124.         pf->text     = &outgoing->subject;
  2125.         break;
  2126.  
  2127.       default:
  2128.         q_status_message1(SM_ORDER,3,7,
  2129.         "Unknown header type %d in pine_send", (void *)pf->type);
  2130.         break;
  2131.     }
  2132.  
  2133.     /*
  2134.      * We may or may not want to give the user the chance to edit
  2135.      * the From and Reply-To lines.  If they are listed in either
  2136.      * Default-composer-hdrs or Customized-hdrs, then they can edit
  2137.      * them, else no.
  2138.      * If canedit is not set, that means that this header is not in
  2139.      * the user's customized-hdrs.  If rich_header is set, that
  2140.      * means that this header is not in the user's
  2141.      * default-composer-hdrs (since From and Reply-To are rich
  2142.      * by default).  So, don't give it an he to edit with in that case.
  2143.      *
  2144.      * For other types, just not setting canedit will cause it to be
  2145.      * uneditable, regardless of what the user does.
  2146.      */
  2147.     switch(index){
  2148.       case N_FROM:
  2149. /* to allow it, we let this fall through to the reply-to case below */
  2150. #ifndef ALLOW_CHANGING_FROM
  2151.         if(pf->canedit || !he->rich_header)
  2152.           q_status_message(SM_ORDER, 3, 3,
  2153.             "Not allowed to change header \"From\"");
  2154.  
  2155.         memset(he, 0, (size_t)sizeof(*he));
  2156.         pf->he = NULL;
  2157.         break;
  2158. #endif
  2159.  
  2160.       case N_REPLYTO:
  2161.         if(!pf->canedit && he->rich_header){
  2162.             memset(he, 0, (size_t)sizeof(*he));
  2163.         pf->he = NULL;
  2164.         }
  2165.         else{
  2166.         pf->canedit = 1;
  2167.         he++;
  2168.         }
  2169.  
  2170.         break;
  2171.  
  2172.       default:
  2173.         if(!pf->canedit){
  2174.             memset(he, 0, (size_t)sizeof(*he));
  2175.         pf->he = NULL;
  2176.         }
  2177.         else
  2178.           he++;
  2179.  
  2180.         break;
  2181.     }
  2182.     }
  2183.  
  2184.     /*
  2185.      * This is so the builder can tell the composer to fill the affected
  2186.      * field based on the value in the field on the left.
  2187.      *
  2188.      * Note that this mechanism isn't completely general.  Each entry has
  2189.      * only a single next_affected, so if some other entry points an
  2190.      * affected entry at an entry with a next_affected, they all inherit
  2191.      * that next_affected.  Since this isn't used much a careful ordering
  2192.      * of the affected fields should make it a sufficient mechanism.
  2193.      */
  2194.     he_to->affected_entry   = he_fcc;
  2195.     he_news->affected_entry = he_fcc;
  2196.     he_lcc->affected_entry  = he_to;
  2197.     he_to->next_affected    = he_fcc;
  2198.  
  2199.     (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
  2200.  
  2201.     i = START_OF_CUSTOM_SENDING_ORDER;
  2202.     /* set up headerentries for custom fields */
  2203.     for(pf = pf->next; pf && pf->name; pf = pf->next, he++){
  2204.     char *addr;
  2205.  
  2206.     pf->he          = he;
  2207.     pf->canedit     = 1;
  2208.     pf->rcptto      = 0;
  2209.     pf->writehdr    = 1;
  2210.     pf->localcopy   = 1;
  2211.     
  2212.     switch(pf->type){
  2213.       case Address:
  2214.         sending_order[i++] = pf;
  2215.         *he = he_custom_addr_templ;
  2216.         /* change default text into an ADDRESS */
  2217.         /* strip quotes around whole default */
  2218.         if(*pf->textbuf == '"'){
  2219.           char *p;
  2220.  
  2221.           /* strip trailing whitespace */
  2222.           p = pf->textbuf + strlen(pf->textbuf) - 1;
  2223.           while(isspace(*p) && p > pf->textbuf)
  2224.         p--;
  2225.  
  2226.           /* this is a set of surrounding quotes we want to strip */
  2227.           if(*p == '"'){
  2228.         *p = '\0';
  2229.         for(p=pf->textbuf; *(p+1); p++)
  2230.           *p = *(p+1);
  2231.  
  2232.         *p = '\0';
  2233.           }
  2234.         }
  2235.  
  2236.         build_address(pf->textbuf, &addr, NULL, NULL);
  2237.         rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
  2238.         fs_give((void **)&addr);
  2239.         if(pf->textbuf)
  2240.           fs_give((void **)&pf->textbuf);
  2241.  
  2242.         he->realaddr = &pf->scratch;
  2243.         break;
  2244.  
  2245.       case FreeText:
  2246.         sending_order[i++] = pf;
  2247.         *he                = he_custom_free_templ;
  2248.         he->realaddr       = &pf->textbuf;
  2249.         pf->text           = &pf->textbuf;
  2250.         break;
  2251.  
  2252.       default:
  2253.         q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
  2254.                             (void *)pf->type);
  2255.         break;
  2256.     }
  2257.  
  2258.     he->name = pf->name;
  2259.  
  2260.     /* use first 8 characters for prompt */
  2261.     he->prompt = cpystr("        : ");
  2262.     strncpy(he->prompt, he->name, min(strlen(he->name), he->prlen - 2));
  2263.  
  2264.     he->rich_header = view_as_rich(he->name, he->rich_header);
  2265.     }
  2266.  
  2267.     /*
  2268.      * Make sure at least *one* field is displayable...
  2269.      */
  2270.     for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
  2271.       if(pf->he && !pf->he->rich_header){
  2272.       index = i;
  2273.       break;
  2274.       }
  2275.  
  2276.     /*
  2277.      * None displayable!!!  Warn and display defaults.
  2278.      */
  2279.     if(index == -1){
  2280.     q_status_message(SM_ORDER,0,5,
  2281.              "No default-composer-hdrs matched, displaying defaults");
  2282.     for(i = 0, pf = header.local; pf; pf = pf->next, i++)
  2283.       if(i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
  2284.         pf->he->rich_header = 0;
  2285.     }
  2286.  
  2287.     /*----------------------------------------------------------------------
  2288.        Loop calling the editor until everything goes well
  2289.      ----*/
  2290.     while(1){
  2291.     /*
  2292.      * set initial cursor position based on how many times we've been
  2293.      * thru the loop...
  2294.      */
  2295.     if((reply_list && reply_list[0] != -1) || body_start){
  2296.         pbuf.pine_flags |= P_BODY;
  2297.         body_start = 0;        /* maybe not next time */
  2298.     }
  2299.     else if(reply_list)
  2300.       pbuf.pine_flags |= P_HEADEND;
  2301.  
  2302.     /* in case these were turned on in previous pass through loop */
  2303.     if(pf_ref){
  2304.         pf_ref->writehdr     = 0;
  2305.         pf_ref->localcopy    = 0;
  2306.     }
  2307.  
  2308.     if(pf_nobody){
  2309.         pf_nobody->writehdr  = 0;
  2310.         pf_nobody->localcopy = 0;
  2311.     }
  2312.  
  2313.     if(pf_fcc)
  2314.       pf_fcc->localcopy = 0;
  2315.  
  2316.     /*
  2317.      * If a sending attempt failed after we passed the message text
  2318.      * thru a user-defined filter, "orig_so" points to the original
  2319.      * text.  Replace the body's encoded data with the original...
  2320.      */
  2321.     if(orig_so){
  2322.         STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
  2323.                 ? &(*body)->contents.part->body.contents.binary
  2324.                 : &(*body)->contents.binary);
  2325.         so_give(so);
  2326.         *so     = orig_so;
  2327.         orig_so = NULL;
  2328.     }
  2329.  
  2330.         /*
  2331.          * Convert the envelope and body to the string format that
  2332.          * pico can edit
  2333.          */
  2334.         outgoing2strings(&header, *body, &pbuf.msgtext, &pbuf.attachments);
  2335.  
  2336.     for(pf = header.local; pf && pf->name; pf = pf->next){
  2337.         /*
  2338.          * If this isn't the first time through this loop, we may have
  2339.          * freed some of the FreeText headers below so that they wouldn't
  2340.          * show up as empty headers in the finished message.  Need to
  2341.          * alloc them again here so they can be edited.
  2342.          */
  2343.         if(pf->type == FreeText && pf->he && !*pf->he->realaddr)
  2344.           *pf->he->realaddr = cpystr("");
  2345.  
  2346.         if(pf->type != Attachment && pf->he && *pf->he->realaddr)
  2347.           pf->he->maxlen = strlen(*pf->he->realaddr);
  2348.     }
  2349.  
  2350. #ifdef _WINDOWS
  2351.     mswin_setwindowmenu (MENU_COMPOSER);
  2352. #endif
  2353. #if    defined(DOS) && !defined(_WINDOWS)
  2354. /*
  2355.  * dumb hack to help ensure we've got something left
  2356.  * to send with if the user runs out of precious memory
  2357.  * in the composer...    (FIX THIS!!!)
  2358.  */
  2359.     if((reserve = (char *)malloc(16384)) == NULL)
  2360.       q_status_message(SM_ORDER | SM_DING, 0, 5,
  2361.                "LOW MEMORY!  May be unable to complete send!");
  2362. #endif
  2363.  
  2364.     cancel_busy_alarm(-1);
  2365.         flush_status_messages(0);
  2366.  
  2367.     dprint(1, (debugfile, "\n  ---- COMPOSER ----\n"));
  2368.     editor_result = pico(&pbuf);
  2369.     dprint(4, (debugfile, "... composer returns (0x%x)\n", editor_result));
  2370.  
  2371. #if    defined(DOS) && !defined(_WINDOWS)
  2372.     free((char *)reserve);
  2373. #endif
  2374. #ifdef _WINDOWS
  2375.     mswin_setwindowmenu (MENU_DEFAULT);
  2376. #endif
  2377.     fix_windsize(ps_global);
  2378.     /*
  2379.      * Only reinitialize signals if we didn't receive an interesting
  2380.      * one while in pico, since pico's return is part of processing that
  2381.      * signal and it should continue to be ignored.
  2382.      */
  2383.     if(!(editor_result & COMP_GOTHUP))
  2384.       init_signals();        /* Pico has it's own signal stuff */
  2385.  
  2386.     /*
  2387.      * We're going to save in DEADLETTER.  Dump attachments first.
  2388.      */
  2389.     if(editor_result & COMP_CANCEL)
  2390.       free_attachment_list(&pbuf.attachments);
  2391.  
  2392.         /* Turn strings back into structures */
  2393.         strings2outgoing(&header, body, pbuf.attachments);
  2394.   
  2395.         /* Make newsgroups NULL if it is "" (so won't show up in headers) */
  2396.     if(outgoing->newsgroups){
  2397.         sqzspaces(outgoing->newsgroups);
  2398.         if(!outgoing->newsgroups[0])
  2399.           fs_give((void **)&(outgoing->newsgroups));
  2400.     }
  2401.  
  2402.         /* Make subject NULL if it is "" (so won't show up in headers) */
  2403.         if(outgoing->subject && !outgoing->subject[0])
  2404.           fs_give((void **)&(outgoing->subject)); 
  2405.     
  2406.     /* remove custom fields that are empty */
  2407.     for(pf = header.local; pf && pf->name; pf = pf->next){
  2408.       if(pf->type == FreeText && pf->textbuf){
  2409.         if(pf->textbuf[0] == '\0'){
  2410.         fs_give((void **)&pf->textbuf); 
  2411.         pf->text = NULL;
  2412.         }
  2413.       }
  2414.     }
  2415.  
  2416.         removing_trailing_white_space(fcc);
  2417.  
  2418.     /*-------- Stamp it with a current date -------*/
  2419.     if(outgoing->date)            /* update old date */
  2420.       fs_give((void **)&(outgoing->date));
  2421.  
  2422.     rfc822_date(tmp_20k_buf);        /* format and copy new date */
  2423.     outgoing->date = cpystr(tmp_20k_buf);
  2424.  
  2425. #ifdef OLDWAY
  2426. /* some people objected to our doing this. */
  2427.     /*------- If Reply-To same as From, get rid of it -------*/
  2428.     if(outgoing->reply_to
  2429.        && address_is_same(outgoing->from, outgoing->reply_to))
  2430.       mail_free_address(&outgoing->reply_to);
  2431. #endif /* OLDWAY
  2432.  
  2433.     /*
  2434.      * Don't ever believe the sender that is there.
  2435.      * If From doesn't look quite right, generate our own sender.
  2436.      */
  2437.     if(outgoing->sender)
  2438.       mail_free_address(&outgoing->sender);
  2439.  
  2440.     /*
  2441.      * If the LHS of the address doesn't match, or the RHS
  2442.      * doesn't match one of localdomain or hostname,
  2443.      * then add a sender line (really X-Sender).
  2444.      *
  2445.      * Don't add a personal_name since the user can change that.
  2446.      */
  2447.     if(!outgoing->from->mailbox ||
  2448.         strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0 ||
  2449.        !outgoing->from->host ||
  2450.         !(strucmp(outgoing->from->host, ps_global->localdomain) == 0 ||
  2451.           strucmp(outgoing->from->host, ps_global->hostname) == 0)){
  2452.  
  2453.         outgoing->sender          = mail_newaddr();
  2454.         outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
  2455.         outgoing->sender->host    = cpystr(ps_global->hostname);
  2456.     }
  2457.  
  2458.         /*----- Message is edited, now decide what to do with it ----*/
  2459.     if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
  2460.             /*=========== Postpone or Interrupted message ============*/
  2461.         CONTEXT_S *fcc_cntxt = NULL;
  2462.         char       folder[MAXPATH+1];
  2463.         int           fcc_result = 0;
  2464.         char       label[50];
  2465.  
  2466.         dprint(4, (debugfile, "pine_send:%s handling\n",
  2467.                (editor_result & COMP_SUSPEND)
  2468.                ? "SUSPEND"
  2469.                : (editor_result & COMP_GOTHUP)
  2470.                    ? "HUP"
  2471.                    : (editor_result & COMP_CANCEL)
  2472.                    ? "CANCEL" : "HUH?"));
  2473.         if((editor_result & COMP_CANCEL)
  2474.            && F_ON(F_QUELL_DEAD_LETTER, ps_global)){
  2475.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  2476.         break;
  2477.         }
  2478.  
  2479.         /*
  2480.          * The idea here is to use the Fcc: writing facility
  2481.          * to append to the special postponed message folder...
  2482.          *
  2483.          * NOTE: the strategy now is to write the message and
  2484.          * all attachments as they exist at composition time.
  2485.          * In other words, attachments are postponed by value
  2486.          * and not reference.  This may change later, but we'll
  2487.          * need a local "message/external-body" type that
  2488.          * outgoing2strings knows how to properly set up for
  2489.          * the composer.  Maybe later...
  2490.          */
  2491.  
  2492.         label[0] = '\0';
  2493.  
  2494.         if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
  2495.            && (editor_result & COMP_SUSPEND)
  2496.            && check_addresses(&header)){
  2497.         /*--- Addresses didn't check out---*/
  2498.         q_status_message(SM_ORDER, 7, 7,
  2499.           "Not allowed to postpone message until addresses are qualified");
  2500.         continue;
  2501.             }
  2502.  
  2503.         /*
  2504.          * Build the local_so.  In the HUP case, we'll write the
  2505.          * bezerk delimiter by hand and output the message directly
  2506.          * into the folder.  It's not only faster, we don't have to
  2507.          * worry about c-client reentrance and less hands paw over
  2508.          * over the data so there's less chance of a problem.
  2509.          *
  2510.          * In the Postpone case, just create it if the user wants to
  2511.          * and create a temporary storage object to write into.
  2512.          */
  2513.         local_written = 0;
  2514.         if(editor_result & COMP_GOTHUP){
  2515.         int    newfile;
  2516.         time_t now = time((time_t *)0);
  2517.  
  2518.         build_path(folder,
  2519.               ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  2520.                           : ps_global->home_dir,
  2521.               INTERRUPTED_MAIL);
  2522.         newfile = can_access(folder, ACCESS_EXISTS);
  2523.         if(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)){
  2524.             sprintf(tmp_20k_buf, "%sFrom %s@%s %.24s\015\012",
  2525.                 newfile ? "" : "\015\012",
  2526.                 outgoing->from->mailbox,
  2527.                 outgoing->from->host, ctime(&now));
  2528.             if(!so_puts(local_so, tmp_20k_buf))
  2529.               dprint(1, (debugfile,"***CAN'T WRITE %s: %s\n",
  2530.                  folder, error_description(errno)));
  2531.         }
  2532.         }
  2533.         else if(editor_result & COMP_SUSPEND){
  2534.         if(!ps_global->VAR_POSTPONED_FOLDER
  2535.            || !ps_global->VAR_POSTPONED_FOLDER[0]){
  2536.             q_status_message(SM_ORDER, 3, 3,
  2537.                      "No postponed file defined");
  2538.             continue;
  2539.         }
  2540.  
  2541.         strcpy(folder, ps_global->VAR_POSTPONED_FOLDER);
  2542.         strcpy(label, "postponed message");
  2543.         local_so = open_fcc(folder,&fcc_cntxt, 1)
  2544.               ? so_get(FCC_SOURCE, NULL, WRITE_ACCESS) : NULL;
  2545.         }
  2546.         else{                /* canceled message */
  2547. #if defined(DOS) || defined(OS2)
  2548.         /*
  2549.          * we can't assume anything about root or home dirs, so
  2550.          * just plunk it down in the same place as the pinerc
  2551.          */
  2552.         if(!getenv("HOME")){
  2553.             char *lc = last_cmpnt(ps_global->pinerc);
  2554.             folder[0] = '\0';
  2555.             if(lc != NULL){
  2556.             strncpy(folder,ps_global->pinerc,lc-ps_global->pinerc);
  2557.             folder[lc - ps_global->pinerc] = '\0';
  2558.             }
  2559.  
  2560.             strcat(folder, DEADLETTER);
  2561.         }
  2562.         else
  2563. #endif
  2564.         build_path(folder,
  2565.             ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  2566.                     : ps_global->home_dir, DEADLETTER);
  2567.  
  2568.         strcpy(label, DEADLETTER);
  2569.         unlink(folder);
  2570.         local_so = open_fcc(folder,&fcc_cntxt, 1)
  2571.               ? so_get(FCC_SOURCE, NULL, WRITE_ACCESS) : NULL;
  2572.         }
  2573.  
  2574.         if(local_so){
  2575.  
  2576.         /* Turn on references header */
  2577.         if(ref_list){
  2578.             pf_ref->writehdr  = 1;
  2579.             pf_ref->localcopy = 1;
  2580.         }
  2581.  
  2582.         /* copy fcc line to postponed or interrupted folder */
  2583.             if(pf_fcc)
  2584.           pf_fcc->localcopy = 1;
  2585.  
  2586.         /*
  2587.          * We need to make sure any header values that got cleared
  2588.          * get written to the postponed message (they won't if
  2589.          * pf->text is NULL).  Otherwise, we can't tell previously
  2590.          * non-existent custom headers or default values from 
  2591.          * custom (or other) headers that got blanked in the
  2592.          * composer...
  2593.          */
  2594.         for(pf = header.local; pf && pf->name; pf = pf->next)
  2595.           if(pf->type == FreeText && pf->he && !*(pf->he->realaddr))
  2596.             *(pf->he->realaddr) = cpystr("");
  2597.  
  2598.         if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0){
  2599.             if(editor_result & COMP_GOTHUP){
  2600.             char    *err;
  2601.             STORE_S *hup_so;
  2602.             gf_io_t     gc, pc;
  2603.  
  2604.             if(hup_so = so_get(FileStar, folder, WRITE_ACCESS)){
  2605.                 gf_set_so_readc(&gc, local_so);
  2606.                 gf_set_so_writec(&pc, hup_so);
  2607.                 so_seek(local_so, 0L, 0);     /* read msg copy and */
  2608.                 so_seek(hup_so, 0L, 2);    /* append to folder  */
  2609.                 gf_filter_init();
  2610.                 gf_link_filter(gf_nvtnl_local);
  2611.                 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
  2612.                   dprint(1, (debugfile, "*** PIPE FAILED: %s\n",
  2613.                      err));
  2614.  
  2615.                 so_give(&hup_so);
  2616.             }
  2617.             else
  2618.               dprint(1, (debugfile, "*** CAN'T CREATE %s: %s\n",
  2619.                      folder, error_description(errno)));
  2620.             }
  2621.             else
  2622.               fcc_result = write_fcc(folder,fcc_cntxt,local_so,label);
  2623.         }
  2624.  
  2625.         so_give(&local_so);
  2626.         }
  2627.         else
  2628.           dprint(1, (debugfile, "***CAN'T ALLOCATE temp store: %s ",
  2629.              error_description(errno)));
  2630.  
  2631.         if(editor_result & COMP_GOTHUP){
  2632.         dprint(1, (debugfile, "Save composition on HUP %sED\n",
  2633.                fcc_result ? "SUCCEED" : "FAIL"));
  2634.         hup_signal();        /* Do what we normally do on SIGHUP */
  2635.         }
  2636.         else if(editor_result & COMP_SUSPEND && fcc_result){
  2637.         q_status_message(SM_ORDER, 0, 3,
  2638.              "Composition postponed. Select Compose to resume.");
  2639.                 break; /* postpone went OK, get out of here */
  2640.         }
  2641.         else if(editor_result & COMP_CANCEL){
  2642.         q_status_message(SM_ORDER, 0, 3, "Message cancelled");
  2643.         break;
  2644.             }
  2645.         else{
  2646.         q_status_message(SM_ORDER, 0, 4,
  2647.             "Continuing composition.  Message not postponed or sent");
  2648.         body_start = 1;
  2649.         continue; /* postpone failed, jump back in to composer */
  2650.             }
  2651.     }
  2652.     else{
  2653.         /*------ Must be sending mail or posting ! -----*/
  2654.         int           news_result, mail_result, fcc_result;
  2655.         CONTEXT_S *fcc_cntxt = NULL;
  2656.  
  2657.         mail_result = news_result = fcc_result = 0;
  2658.         dprint(4, (debugfile, "=== sending: "));
  2659.  
  2660.             /* --- If posting, confirm with user ----*/
  2661.         if(outgoing->newsgroups && *outgoing->newsgroups
  2662.            && want_to(POST_PMT, 'n', 'n', NO_HELP, 0, 0) == 'n'){
  2663.         q_status_message(SM_ORDER, 0, 3, "Message not posted");
  2664.         dprint(4, (debugfile, "no post, continuing\n"));
  2665.         continue;
  2666.         }
  2667.  
  2668.         if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
  2669.          || outgoing->newsgroups || (fcc && fcc[0]))){
  2670.         q_status_message(SM_ORDER, 3, 4, "No recipients specified!");
  2671.         dprint(4, (debugfile, "no recip, continuing\n"));
  2672.         continue;
  2673.         }
  2674.  
  2675.         if(check_addresses(&header)){
  2676.         /*--- Addresses didn't check out---*/
  2677.            dprint(4, (debugfile, "addrs failed, continuing\n"));
  2678.            continue;
  2679.            }
  2680.  
  2681.         set_last_fcc(fcc);
  2682.  
  2683.             /*---- Check out fcc -----*/
  2684.             if(fcc && *fcc){
  2685.         local_written = 0;
  2686.             if(!open_fcc(fcc, &fcc_cntxt, 0)
  2687.            || !(local_so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS))){
  2688.             /* ---- Open or allocation of fcc failed ----- */
  2689.                     q_status_message(SM_ORDER, 3, 5,
  2690.                      "Message NOT sent or written to fcc.");
  2691.             dprint(4, (debugfile,"can't open/allocate fcc, cont'g\n"));
  2692.  
  2693.             /*
  2694.              * Find field entry associated with fcc, and start
  2695.              * composer on it...
  2696.              */
  2697.             for(pf = header.local; pf && pf->name; pf = pf->next)
  2698.               if(pf->type == Fcc && pf->he)
  2699.             pf->he->start_here = 1;
  2700.  
  2701.             continue;
  2702.         }
  2703.             }
  2704.         else
  2705.           local_so = NULL;
  2706.  
  2707.             /*---- recompute message-id to encode body info stats ----*/
  2708.             update_message_id(outgoing, mime_stats(*body));
  2709.  
  2710.             /*---- Take care of any requested prefiltering ----*/
  2711.         if(sending_filter_requested
  2712.            && !filter_message_text(sending_filter_requested, outgoing,
  2713.                        *body, &orig_so))
  2714.         continue;
  2715.  
  2716.             /*------ Actually post  -------*/
  2717.             if(outgoing->newsgroups){
  2718.         /* Turn on references header */
  2719.         if(ref_list){
  2720.             pf_ref->writehdr  = 1;
  2721.             pf_ref->localcopy = 1;
  2722.         }
  2723.  
  2724.         news_result = news_poster(&header, *body); /* try posting */
  2725.  
  2726.         if(news_result < 0){
  2727.             dprint(1, (debugfile, "Post failed, continuing\n"));
  2728.             continue;
  2729.         }
  2730.         }
  2731.  
  2732.         /*
  2733.          * BUG: IF we've posted the message *and* an fcc was specified
  2734.          * then we've already got a neatly formatted message in the
  2735.          * local_so.  It'd be nice not to have to re-encode everything
  2736.          * to insert it into the smtp slot...
  2737.          */
  2738.  
  2739.         /*
  2740.          * Turn on "undisclosed recipients" header if no To or cc.
  2741.          */
  2742.             if(!(outgoing->to || outgoing->cc || outgoing->newsgroups)
  2743.           && (outgoing->bcc || lcc_addr) && pf_nobody){
  2744.         pf_nobody->writehdr  = 1;
  2745.         pf_nobody->localcopy = 1;
  2746.         }
  2747.  
  2748.             /*------- Actually mail the message ------*/
  2749.             if(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)
  2750.           mail_result = call_mailer(&header, *body);
  2751.  
  2752.         /*----- Was there an fcc involved? -----*/
  2753.             if(local_so){
  2754.         /*------ Write it if at least something worked ------*/
  2755.         if(mail_result == 1 || news_result == 1
  2756.            || (mail_result == 0 && news_result == 0
  2757.                && pine_rfc822_output(&header, *body, NULL,NULL))){
  2758.             char label[50];
  2759.  
  2760.             strcpy(label, "Fcc");
  2761.             if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC))
  2762.               sprintf(label + 3, " to %.40s", fcc);
  2763.  
  2764.             /*-- Now actually copy to fcc folder and close --*/
  2765.             fcc_result = write_fcc(fcc, fcc_cntxt, local_so, label);
  2766.         }
  2767.         else if(mail_result == 0 && news_result == 0){
  2768.             q_status_message(SM_ORDER,3,5,
  2769.             "Fcc Failed!.  No message saved.");
  2770.             dprint(1, (debugfile, "explicit fcc write failed!\n"));
  2771.         }
  2772.  
  2773.         so_give(&local_so);
  2774.         }
  2775.  
  2776.             /*----- Both weren't OK, back to composer -----*/
  2777.             if(mail_result < 0 && news_result != 1){
  2778.         dprint(1, (debugfile, "Send and Post failed, continuing\n"));
  2779.         continue;
  2780.         }
  2781.  
  2782.             /*----- Signed, sealed, delivered! ------*/
  2783.             q_status_message7(SM_ORDER | ((news_result == -1
  2784.                        || mail_result == -1) ? SM_DING :0),
  2785.                   mail_result < 0 ? 3 : 0, 3,
  2786.                               "Message %s%s%s%s%s%s%s.",
  2787.                               (news_result == 1)
  2788.                     ? "posted" 
  2789.                     : (news_result == -1)
  2790.                     ? "NOT posted" : "",
  2791.                   (news_result && mail_result && fcc_result)
  2792.                     ? ", "
  2793.                     : (news_result && mail_result)
  2794.                         ? " and " : "",
  2795.                               (mail_result == 1)
  2796.                     ? "sent"
  2797.                     :  (mail_result == -1)
  2798.                          ? "NOT SENT" : "",
  2799.                   ((mail_result || news_result) && fcc_result)
  2800.                     ? " and copied to " 
  2801.                     : (fcc_result) ? "ONLY copied to " : "",
  2802.                               (fcc_result) ? "\"" : "",
  2803.                               (fcc_result) ? fcc  : "",
  2804.                               (fcc_result) ? "\"" : "");
  2805.  
  2806.         /*
  2807.          * If message sent *completely* successfully, there's a
  2808.          * reply_list AND we're allowed to write back state, do it.
  2809.          * But also protect against shifted message numbers due 
  2810.          * to new mail arrival.  Since the number passed is based
  2811.          * on the real imap msg no, AND we're sure no expunge has 
  2812.          * been done, just fix up the sorted number...
  2813.          */
  2814.         if(reply_list && reply_list[0] > -1 && !READONLY_FOLDER){
  2815.         char *seq, *p;
  2816.         long  i, j;
  2817.  
  2818.         for(i = 0L, p = tmp_20k_buf; reply_list[i] != -1L; i++){
  2819.             if(i)
  2820.               sstrcpy(&p, ",");
  2821.  
  2822.             sstrcpy(&p, long2string(reply_list[i]));
  2823.             if(j = mn_raw2m(ps_global->msgmap, reply_list[i]))
  2824.               clear_index_cache_ent(j);
  2825.         }
  2826.  
  2827.         seq = cpystr(tmp_20k_buf);
  2828.         mail_setflag(ps_global->mail_stream, seq, "\\ANSWERED");
  2829.         fs_give((void **)&seq);
  2830.         check_point_change();
  2831.         }
  2832.  
  2833.             if(mail_result < 0){
  2834.         /* Send wasn't OK, back to the composer */
  2835.         dprint(1, (debugfile, "Send failed, continuing\n"));
  2836.         continue;
  2837.         }
  2838.  
  2839.             break; /* All's well, pop out of here */
  2840.  
  2841.             /*--- Send failed, loop on back ----*/
  2842.         }
  2843.     }
  2844.  
  2845.     if(orig_so)
  2846.       so_give(&orig_so);
  2847.  
  2848.     if(fcc)
  2849.       fs_give((void **)&fcc);
  2850.  
  2851.     free_attachment_list(&pbuf.attachments);
  2852.     for(i=0; i < fixed_cnt; i++){
  2853.     if(pfields[i].textbuf)
  2854.       fs_give((void **)&pfields[i].textbuf);
  2855.  
  2856.     fs_give((void **)&pfields[i].name);
  2857.     }
  2858.  
  2859.     if(lcc_addr)
  2860.       mail_free_address(&lcc_addr);
  2861.     
  2862.     free_customs(header.custom);
  2863.     fs_give((void **)&pfields);
  2864.     fs_give((void **)&headents);
  2865.     fs_give((void **)&sending_order);
  2866.     dprint(4, (debugfile, "=== send returning ===\n"));
  2867. }
  2868.  
  2869.  
  2870.  
  2871. /*----------------------------------------------------------------------
  2872.    Check for addresses the user is not permitted to send to, or probably
  2873.    doesn't want to send to
  2874.    
  2875. Returns: 0 if OK, and 1 if the message shouldn't be sent
  2876.  
  2877. Queues a message indicating what happened
  2878.   ---*/
  2879. int
  2880. check_addresses(header)
  2881.     METAENV *header;
  2882. {
  2883.     PINEFIELD *pf;
  2884.     ADDRESS *a;
  2885.     int         send_daemon = 0;
  2886.  
  2887.     /*---- Is he/she trying to send mail to the mailer-daemon ----*/
  2888.     for(pf = header->local; pf && pf->name; pf = pf->next)
  2889.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  2890.     for(a = *pf->addr; a != NULL; a = a->next){
  2891.         if(a->host && (a->host[0] == '.'
  2892.                || (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
  2893.                    && a->host[0] == '@'))){
  2894.         q_status_message2(SM_ORDER, 4, 7,
  2895.                   "Can't send to address %s: %s",
  2896.                   a->mailbox,
  2897.                   (a->host[0] == '.')
  2898.                     ? a->host
  2899.                     : "not in addressbook");
  2900.         return(1);
  2901.         }
  2902.         else if(ps_global->restricted
  2903.             && !address_is_us(*pf->addr, ps_global)){
  2904.         q_status_message(SM_ORDER, 3, 3,
  2905.     "Restricted demo version of Pine. You may only send mail to yourself");
  2906.         return(1);
  2907.         }
  2908.         else if(a->mailbox && strucmp(a->mailbox, "mailer-daemon") == 0
  2909.             && !send_daemon){
  2910.         send_daemon = 1;
  2911.         if(want_to("Really send this message to the MAILER-DAEMON",
  2912.                'n', 'n', NO_HELP, 0, 0) == 'n')
  2913.           return(1);
  2914.         }
  2915.     }
  2916.  
  2917.     return(0);
  2918. }
  2919.  
  2920.  
  2921. /*----------------------------------------------------------------------
  2922.     Validate the given subject relative to any news groups.
  2923.      
  2924. Args: none
  2925.  
  2926. Returns: always returns 1, but also returns error if
  2927. ----*/      
  2928. int
  2929. valid_subject(given, expanded, error, fcc)
  2930.     char     *given,
  2931.         **expanded,
  2932.         **error;
  2933.     BUILDER_ARG  *fcc;
  2934. {
  2935.     struct headerentry *hp;
  2936.  
  2937.     if(expanded)
  2938.       *expanded = cpystr(given);
  2939.  
  2940.     if(error){
  2941.     /*
  2942.      * Now look for any header entry we passed to pico that has to do
  2943.      * with news.  If there's no subject, gripe.
  2944.      */
  2945.     for(hp = pbuf.headents; hp->prompt; hp++)
  2946.       if(hp->help == h_composer_news){
  2947.           if(hp->hd_text->text[0] && !*given)
  2948.         *error = cpystr(
  2949.             "News postings MUST have a subject!  Please add one!");
  2950.  
  2951.           break;
  2952.       }
  2953.     }
  2954.  
  2955.     return(0);
  2956. }
  2957.  
  2958.  
  2959.  
  2960. /*----------------------------------------------------------------------
  2961.     Call to map pine's flags into those pico makes available
  2962.  
  2963. Args: ps -- usual pine structure
  2964.  
  2965. Returns: long contining bitmap of pico flags
  2966. ----*/      
  2967. long
  2968. flags_for_pico(ps)
  2969.     struct pine *ps;
  2970. {
  2971.     return((F_ON(F_CAN_SUSPEND, ps)        ? P_SUSPEND : 0L)
  2972.        | (F_ON(F_USE_FK,ps)            ? P_FKEYS : 0L)
  2973.        | (ps->restricted            ? P_SECURE : 0L)
  2974.        | (F_ON(F_ALT_ED_NOW,ps)        ? P_ALTNOW : 0L)
  2975.        | (F_ON(F_USE_CURRENT_DIR,ps)    ? P_CURDIR : 0L)
  2976.        | (F_ON(F_SUSPEND_SPAWNS,ps)        ? P_SUBSHELL : 0L)
  2977.        | (F_ON(F_COMPOSE_MAPS_DEL,ps)    ? P_DELRUBS : 0L)
  2978.        | (F_ON(F_ENABLE_TAB_COMPLETE,ps)    ? P_COMPLETE : 0L)
  2979.        | (F_ON(F_SHOW_CURSOR, ps)        ? P_SHOCUR : 0L)
  2980.        | (F_ON(F_DEL_FROM_DOT, ps)        ? P_DOTKILL : 0L)
  2981.        | (F_ON(F_ENABLE_DOT_FILES, ps)    ? P_DOTFILES : 0L)
  2982.        | ((F_ON(F_ENABLE_ALT_ED,ps) || F_ON(F_ALT_ED_NOW,ps))
  2983.             ? P_ADVANCED : 0L)
  2984.        | ((!ps->VAR_CHAR_SET || !strucmp(ps->VAR_CHAR_SET, "US-ASCII"))
  2985.             ? P_HIBITIGN: 0L));
  2986. }
  2987.  
  2988.  
  2989.  
  2990. /*----------------------------------------------------------------------
  2991.     Call back for pico to use to check for new mail.
  2992.      
  2993. Args: cursor -- pointer to in to tell caller if cursor location changed
  2994.                 if NULL, turn off cursor positioning.
  2995.       timing -- whether or not it's a good time to check 
  2996.  
  2997.  
  2998. Returns: returns 1 on success, zero on error.
  2999. ----*/      
  3000. long
  3001. new_mail_for_pico(timing, status)
  3002.     int  timing;
  3003.     int  status;
  3004. {
  3005.     int old_cue, rv;
  3006.  
  3007.     /*
  3008.      * If we're not interested in the status, don't display the busy
  3009.      * cue either...
  3010.      */
  3011.     if(!status){
  3012.     old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
  3013.     F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
  3014.     }
  3015.  
  3016.     /* don't know where the cursor's been, reset it */
  3017.     clear_cursor_pos();
  3018.     rv = new_mail(0, timing, status);
  3019.  
  3020.     if(!status)
  3021.       F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
  3022.  
  3023.     return(rv);
  3024. }
  3025.  
  3026.  
  3027.  
  3028.  
  3029. /*----------------------------------------------------------------------
  3030.     Call back for pico to insert the specified message's text
  3031.      
  3032. Args: n -- message number to format
  3033.       f -- function to use to output the formatted message
  3034.  
  3035.  
  3036. Returns: returns msg number formatted on success, zero on error.
  3037. ----*/      
  3038. long
  3039. message_format_for_pico(n, f)
  3040.     long n;
  3041.     int  (*f) PROTO((int));
  3042. {
  3043.     ENVELOPE *e;
  3044.     BODY     *b;
  3045.     char     *old_quote = NULL;
  3046.     long      rv = n;
  3047.  
  3048.     if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
  3049.        && (e = mail_fetchstructure(ps_global->mail_stream,
  3050.                    mn_m2raw(ps_global->msgmap, n), &b)))){
  3051.     q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
  3052.     flush_status_messages(0);
  3053.     return(0L);
  3054.     }
  3055.  
  3056.     /* temporarily assign a new quote string */
  3057.     old_quote = pbuf.quote_str;
  3058.     pbuf.quote_str = reply_quote_str(e, 1);
  3059.  
  3060.     /* build separator line */
  3061.     reply_delimiter(e, f);
  3062.  
  3063.     /* actually write message text */
  3064.     if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b,
  3065.                FM_NEW_MESS, f)){
  3066.     q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
  3067.     flush_status_messages(0);
  3068.     rv = 0L;
  3069.     }
  3070.  
  3071.     fs_give((void **)&pbuf.quote_str);
  3072.     pbuf.quote_str = old_quote;
  3073.     return(rv);
  3074. }
  3075.  
  3076.  
  3077.  
  3078. /*----------------------------------------------------------------------
  3079.     Call back for pico to prompt the user for exit confirmation
  3080.  
  3081. Args: dflt -- default answer for confirmation prompt
  3082.  
  3083. Returns: either NULL if the user accepts exit, or string containing
  3084.      reason why the user declined.
  3085. ----*/      
  3086. char *
  3087. send_exit_for_pico()
  3088. {
  3089.     int           i, rv, c, verbose_label = 0, old_suspend;
  3090.     char      *rstr = NULL, *p;
  3091.     void     (*redraw)() = ps_global->redrawer;
  3092.     ESCKEY_S   opts[8];
  3093.     struct filters {
  3094.     char  *filter;
  3095.     int    index;
  3096.     struct filters *prev, *next;
  3097.     } *filters = NULL, *fp;
  3098.  
  3099.     sending_filter_requested = NULL;
  3100.     if(old_suspend = F_ON(F_CAN_SUSPEND, ps_global))
  3101.       F_SET(F_CAN_SUSPEND, ps_global, 0);
  3102.  
  3103.     /*
  3104.      * Build list of available filters...
  3105.      */
  3106.     for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
  3107.     for(p = ps_global->VAR_SEND_FILTER[i]; *p && !isspace(*p); p++)
  3108.       ;
  3109.  
  3110.     c  = *p;
  3111.     *p = '\0';
  3112.     if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
  3113.          && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
  3114.         *p = c;
  3115.         continue;
  3116.     }
  3117.  
  3118.     fp       = (struct filters *)fs_get(sizeof(struct filters));
  3119.     fp->index  = i;
  3120.     if(strlen(ps_global->VAR_SEND_FILTER[i]) > 20){
  3121.         sprintf(tmp_20k_buf, "...%s", p - 17);
  3122.         fp->filter = cpystr(tmp_20k_buf);
  3123.     }
  3124.     else
  3125.       fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
  3126.  
  3127.     *p = c;
  3128.  
  3129.     if(filters){
  3130.         fp->next       = filters;
  3131.         fp->prev       = filters->prev;
  3132.         fp->prev->next = filters->prev = fp;
  3133.     }
  3134.     else{
  3135.         filters = (struct filters *)fs_get(sizeof(struct filters));
  3136.         filters->index  = -1;
  3137.         filters->filter = NULL;
  3138.         filters->next = filters->prev = fp;
  3139.         fp->next = fp->prev = filters;
  3140.     }
  3141.     }
  3142.  
  3143.     i = 0;
  3144.     opts[i].ch      = 'y';
  3145.     opts[i].rval    = 'y';
  3146.     opts[i].name    = "Y";
  3147.     opts[i++].label = "Yes";
  3148.  
  3149.     opts[i].ch      = 'n';
  3150.     opts[i].rval    = 'n';
  3151.     opts[i].name    = "N";
  3152.     opts[i++].label = "No";
  3153.  
  3154.     if(filters){
  3155.     /* set global_filter_pointer to desired filter or NULL if none */
  3156.     /* prepare two keymenu slots for selecting filter */
  3157.     opts[i].ch      = ctrl('P');
  3158.     opts[i].rval    = 10;
  3159.     opts[i].name    = "^P";
  3160.     opts[i++].label = "Prev Filter";
  3161.  
  3162.     opts[i].ch      = ctrl('N');
  3163.     opts[i].rval    = 11;
  3164.     opts[i].name    = "^N";
  3165.     opts[i++].label = "Next Filter";
  3166.  
  3167.     if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
  3168.       filters = filters->next;
  3169.     }
  3170.  
  3171.     verbose_requested = 0;
  3172.     if(F_ON(F_VERBOSE_POST, ps_global)){
  3173.     /* setup keymenu slot to toggle verbose mode */
  3174.     opts[i].ch    = ctrl('W');
  3175.     opts[i].rval  = 12;
  3176.     opts[i].name  = "^W";
  3177.     verbose_label = i++;
  3178.     }
  3179.  
  3180.     if(filters){
  3181.     opts[i].ch      = KEY_UP;
  3182.     opts[i].rval    = 10;
  3183.     opts[i].name    = "";
  3184.     opts[i++].label = "";
  3185.  
  3186.     opts[i].ch      = KEY_DOWN;
  3187.     opts[i].rval    = 11;
  3188.     opts[i].name    = "";
  3189.     opts[i++].label = "";
  3190.     }
  3191.  
  3192.     opts[i].ch = -1;
  3193.  
  3194.     ps_global->redrawer = NULL;
  3195.     fix_windsize(ps_global);
  3196.  
  3197.     while(1){
  3198.     if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
  3199.       *p = '\0';
  3200.     else
  3201.       p = NULL;
  3202.  
  3203.     sprintf(tmp_20k_buf, "Send message%s%s%s%s%s%s%s? ",
  3204.         (filters || verbose_requested) ? " (" : "",
  3205.         (filters && filters->filter) ? "filtered thru \"" : "",
  3206.         (filters) 
  3207.           ? (filters->filter
  3208.               ? filters->filter
  3209.               : "unfiltered")
  3210.           : "",
  3211.         (filters && filters->filter) ? "\"" : "",
  3212.         (filters && verbose_requested) ? " " : "",
  3213.         (verbose_requested) ? "in verbose mode" : "",
  3214.         (filters || verbose_requested) ? ")" : "");
  3215.  
  3216.     if(p)
  3217.       *p = ' ';
  3218.  
  3219.     if(verbose_label)
  3220.       opts[verbose_label].label = verbose_requested ? "Normal" : "Verbose";
  3221.  
  3222. /* BUG: fix resize during prompt!! */
  3223. /* BUG: test kmpopped stuff? */
  3224.     rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
  3225.                'y', 'x', NO_HELP, RB_NORM);
  3226.     if(rv == 'y'){                /* user ACCEPTS! */
  3227.         break;
  3228.     }
  3229.     else if(rv == 'n'){            /* Declined! */
  3230.         rstr = "No Message Sent";
  3231.         break;
  3232.     }
  3233.     else if(rv == 'x'){            /* Cancelled! */
  3234.         rstr = "Send Cancelled";
  3235.         break;
  3236.     }
  3237.     else if(rv == 10)            /* PREVIOUS filter */
  3238.       filters = filters->prev;
  3239.     else if(rv == 11)            /* NEXT filter */
  3240.       filters = filters->next;
  3241.     else if(rv == 12)            /* flip verbose bit */
  3242.       verbose_requested = !verbose_requested;
  3243.     }
  3244.  
  3245.     /* remember selection */
  3246.     if(filters && filters->index > -1)
  3247.       sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
  3248.  
  3249.     if(filters){
  3250.     filters->prev->next = NULL;            /* tie off list */
  3251.     while(filters){                /* then free it */
  3252.         fp = filters->next;
  3253.         if(filters->filter)
  3254.           fs_give((void **)&filters->filter);
  3255.  
  3256.         fs_give((void **)&filters);
  3257.         filters = fp;
  3258.     }
  3259.     }
  3260.  
  3261.     if(old_suspend)
  3262.       F_SET(F_CAN_SUSPEND, ps_global, 1);
  3263.  
  3264.     ps_global->redrawer = redraw;
  3265.     return(rstr);
  3266. }
  3267.  
  3268.  
  3269.  
  3270. /*----------------------------------------------------------------------
  3271.     Call back for pico to get newmail status messages displayed
  3272.  
  3273. Args: x -- char processed
  3274.  
  3275. Returns: 
  3276. ----*/      
  3277. int
  3278. display_message_for_pico(x)
  3279.     int x;
  3280. {
  3281.     clear_cursor_pos();            /* can't know where cursor is */
  3282.     mark_status_dirty();        /* don't count on cached text */
  3283.     return(display_message(x));
  3284. }
  3285.  
  3286.  
  3287.  
  3288. /*----------------------------------------------------------------------
  3289.     Call back for pico to get desired directory for its check point file
  3290.      
  3291.   Args: s -- buffer to write directory name
  3292.     n -- length of that buffer
  3293.  
  3294.   Returns: pointer to static buffer
  3295. ----*/      
  3296. char *
  3297. checkpoint_dir_for_pico(s, n)
  3298.     char *s;
  3299.     int   n;
  3300. {
  3301. #if defined(DOS) || defined(OS2)
  3302.     /*
  3303.      * we can't assume anything about root or home dirs, so
  3304.      * just plunk it down in the same place as the pinerc
  3305.      */
  3306.     if(!getenv("HOME")){
  3307.     char *lc = last_cmpnt(ps_global->pinerc);
  3308.  
  3309.     if(lc != NULL){
  3310.         strncpy(s, ps_global->pinerc, min(n-1,lc-ps_global->pinerc));
  3311.         s[min(n-1,lc-ps_global->pinerc)] = '\0';
  3312.     }
  3313.     else{
  3314.         strncpy(s, ".\\", n-1);
  3315.         s[n-1] = '\0';
  3316.     }
  3317.     }
  3318.     else
  3319. #endif
  3320.     strcpy(s, ps_global->home_dir);
  3321.  
  3322.     return(s);
  3323. }
  3324.  
  3325.  
  3326. /*----------------------------------------------------------------------
  3327.     Call back for pico to display mime type of attachment
  3328.      
  3329. Args: file -- filename being attached
  3330.  
  3331. Returns: returns 1 on success (message queued), zero otherwise (don't know
  3332.       type so nothing queued).
  3333. ----*/      
  3334. int
  3335. mime_type_for_pico(file)
  3336.     char *file;
  3337. {
  3338.     BODY *body;
  3339.     int   rv;
  3340.     void *file_contents;
  3341.  
  3342.     body           = mail_newbody();
  3343.     body->type     = TYPEOTHER;
  3344.     body->encoding = ENCOTHER;
  3345.  
  3346.     /* don't know where the cursor's been, reset it */
  3347.     clear_cursor_pos();
  3348.     if(!set_mime_type_by_extension(body, file)){
  3349.     if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS)) != NULL){
  3350.         body->contents.binary = file_contents;
  3351.         set_mime_type_by_grope(body);
  3352.     }
  3353.     }
  3354.  
  3355.     if(body->type != TYPEOTHER){
  3356.     rv = 1;
  3357.     q_status_message3(SM_ORDER, 0, 3,
  3358.         "File %s attached as type %s/%s", file,
  3359.         body_types[body->type],
  3360.         body->subtype ? body->subtype : rfc822_default_subtype(body->type));
  3361.     }
  3362.     else
  3363.       rv = 0;
  3364.  
  3365.     pine_free_body(&body);
  3366.     return(rv);
  3367. }
  3368.  
  3369.  
  3370.  
  3371. /*----------------------------------------------------------------------
  3372.   Call back for pico to receive an uploaded message
  3373.  
  3374.   Args: fname -- name for uploaded file (empty if they want us to assign it)
  3375.     size -- pointer to long to hold the attachment's size
  3376.  
  3377.   Notes: the attachment is uploaded to a temp file, and 
  3378.  
  3379.   Returns: TRUE on success, FALSE otherwise
  3380. ----*/
  3381. int
  3382. upload_msg_to_pico(fname, size)
  3383.     char *fname;
  3384.     long *size;
  3385. {
  3386.     char     cmd[MAXPATH+1], prefix[1024], *fnp;
  3387.     long     l;
  3388.     PIPE_S  *syspipe;
  3389.  
  3390.     dprint(1, (debugfile, "Upload cmd called to xfer \"%s\"\n",
  3391.            fname ? fname : "<NO FILE>"));
  3392.  
  3393.     if(!fname)                /* no place for file name */
  3394.       return(0);
  3395.  
  3396.     if(!*fname){            /* caller wants temp file */
  3397.     strcpy(fname, fnp = temp_nam(NULL, "pu"));
  3398.     fs_give((void **)&fnp);
  3399.     }
  3400.  
  3401.     build_updown_cmd(cmd, ps_global->VAR_UPLOAD_CMD_PREFIX,
  3402.              ps_global->VAR_UPLOAD_CMD, fname);
  3403.     if(syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET)){
  3404.     (void) close_system_pipe(&syspipe);
  3405.     if((l = name_file_size(fname)) < 0L){
  3406.         q_status_message2(SM_ORDER | SM_DING, 3, 4,
  3407.                   "Error determining size of %s: %s", fname,
  3408.                   fnp = error_description(errno));
  3409.         dprint(1, (debugfile,
  3410.                "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
  3411.                cmd, fname, fnp));
  3412.     }
  3413.     else if(size)
  3414.       *size = l;
  3415.  
  3416.     return(l >= 0);
  3417.     }
  3418.     else
  3419.       q_status_message(SM_ORDER | SM_DING, 3, 4, "Error opening pipe");
  3420.  
  3421.     return(0);
  3422. }
  3423.  
  3424.  
  3425.  
  3426. /*----------------------------------------------------------------------
  3427.   Call back for pico to tell us the window size's changed
  3428.  
  3429.   Args: none
  3430.  
  3431.   Returns: none (but pine's ttyo structure may have been updated)
  3432. ----*/
  3433. void
  3434. resize_for_pico()
  3435. {
  3436.     fix_windsize(ps_global);
  3437. }
  3438.  
  3439.  
  3440.  
  3441.  
  3442. /*----------------------------------------------------------------------
  3443.     Pass the first text segment of the message thru the "send filter"
  3444.      
  3445. Args: body pointer and address for storage object of old data
  3446.  
  3447. Returns: returns 1 on success, zero on error.
  3448. ----*/      
  3449. int
  3450. filter_message_text(fcmd, outgoing, body, old)
  3451.     char      *fcmd;
  3452.     ENVELOPE  *outgoing;
  3453.     BODY      *body;
  3454.     STORE_S  **old;
  3455. {
  3456.     char     *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL;
  3457.     int          key = 0, rv;
  3458.     gf_io_t   gc, pc;
  3459.     STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
  3460.                 ? &body->contents.part->body.contents.binary
  3461.                 : &body->contents.binary),
  3462.          *tmp_so = NULL, *tmpf_so;
  3463.  
  3464.     if(fcmd && (cmd=expand_filter_tokens(fcmd,outgoing,&tmpf,&resultf,&key))){
  3465.     if(tmpf){
  3466.         if(tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS)){
  3467. #ifndef    DOS
  3468.         chmod(tmpf, 0600);
  3469. #endif
  3470.         so_seek(*so, 0L, 0);
  3471.         gf_set_so_readc(&gc, *so);
  3472.         gf_set_so_writec(&pc, tmpf_so);
  3473.         gf_filter_init();
  3474.         if(key){
  3475.             so_puts(tmpf_so, filter_session_key());
  3476.             so_puts(tmpf_so, NEWLINE);
  3477.         }
  3478.  
  3479.         errstr = gf_pipe(gc, pc);
  3480.         so_give(&tmpf_so);
  3481.         }
  3482.         else
  3483.           errstr = "Can't create space for filter temporary file.";
  3484.     }
  3485.  
  3486.     if(!errstr){
  3487.         if(tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)){
  3488.         gf_set_so_writec(&pc, tmp_so);
  3489.         ps_global->mangled_screen = 1;
  3490.         suspend_busy_alarm();
  3491.         ClearScreen();
  3492.         fflush(stdout);
  3493.         if(tmpf){
  3494.             PIPE_S *fpipe;
  3495.  
  3496.             if(fpipe = open_system_pipe(cmd, NULL, NULL,
  3497.                         PIPE_NOSHELL | PIPE_RESET)){
  3498.             if(close_system_pipe(&fpipe) == 0){
  3499.                 if(tmpf_so = so_get(FileStar, tmpf, READ_ACCESS)){
  3500.                 gf_set_so_readc(&gc, tmpf_so);
  3501.                 gf_filter_init();
  3502.                 errstr = gf_pipe(gc, pc);
  3503.                 so_give(&tmpf_so);
  3504.                 }
  3505.                 else
  3506.                   errstr = "Can't open temp file filter wrote.";
  3507.             }
  3508.             else
  3509.               errstr = "Filter command returned error.";
  3510.             }
  3511.             else
  3512.               errstr = "Can't exec filter text.";
  3513.         }
  3514.         else
  3515.           errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
  3516.                      *so, pc, NULL);
  3517.  
  3518.         if(errstr){
  3519.             int ch;
  3520.  
  3521.             fprintf(stdout, "\r\n%s  Hit return to continue.", errstr);
  3522.             fflush(stdout);
  3523.             while((ch = read_char(300)) != ctrl('M')
  3524.               && ch != NO_OP_IDLE)
  3525.               putchar(BELL);
  3526.         }
  3527.         else{
  3528.             BODY *b = (body->type == TYPEMULTIPART)
  3529.                        ? &body->contents.part->body : body;
  3530.  
  3531.             *old = *so;            /* save old so */
  3532.             *so = tmp_so;        /* return new one */
  3533.  
  3534.             /*
  3535.              * Reevaluate the encoding in case the data form's
  3536.              * changed...
  3537.              */
  3538.             b->encoding = ENCOTHER;
  3539.             set_mime_type_by_grope(b);
  3540.         }
  3541.  
  3542.         ClearScreen();
  3543.         resume_busy_alarm();
  3544.         }
  3545.         else
  3546.           errstr = "Can't create space for filtered text.";
  3547.     }
  3548.  
  3549.     fs_give((void **)&cmd);
  3550.     }
  3551.     else
  3552.       return(rv == 0);
  3553.  
  3554.     if(tmpf){
  3555.     unlink(tmpf);
  3556.     fs_give((void **)&tmpf);
  3557.     }
  3558.  
  3559.     if(resultf){
  3560.     if(name_file_size(resultf) > 0L)
  3561.       display_output_file(resultf, "Filter", NULL);
  3562.  
  3563.     fs_give((void **)&resultf);
  3564.     }
  3565.     else if(errstr){
  3566.     if(tmp_so)
  3567.       so_give(&tmp_so);
  3568.  
  3569.     q_status_message1(SM_ORDER | SM_DING, 3, 6, "Problem filtering: %s",
  3570.               errstr);
  3571.     dprint(1, (debugfile, "Filter FAILED: %s\n", errstr));
  3572.     }
  3573.  
  3574.     return(errstr == NULL);
  3575. }
  3576.  
  3577.  
  3578.  
  3579. /*----------------------------------------------------------------------
  3580.      Generate and send a message back to the pine development team
  3581.      
  3582. Args: none
  3583.  
  3584. Returns: none
  3585. ----*/      
  3586. void
  3587. phone_home()
  3588. {
  3589.     int          loser;
  3590.     char      tmp[MAX_ADDRESS], *from_addr;
  3591.     ENVELOPE *outgoing;
  3592.     BODY     *body;
  3593.  
  3594. #if defined(DOS) || defined(OS2)
  3595.     if(!dos_valid_from(0))
  3596.       return;
  3597. #endif
  3598.  
  3599.     outgoing = mail_newenvelope();
  3600.     sprintf(tmp, "pine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
  3601.     rfc822_parse_adrlist(&outgoing->to, tmp, ps_global->maildomain);
  3602.     outgoing->message_id  = generate_message_id(ps_global);
  3603.     outgoing->subject      = cpystr("Document Request");
  3604.  
  3605.     body       = mail_newbody();
  3606.     body->type = TYPETEXT;
  3607.  
  3608.     if(body->contents.binary = (void *)so_get(PicoText,NULL,EDIT_ACCESS)){
  3609.     so_puts((STORE_S *)body->contents.binary, "Document request: Pine");
  3610.     so_puts((STORE_S *)body->contents.binary, PHONE_HOME_VERSION);
  3611.     if(ps_global->first_time_user)
  3612.       so_puts((STORE_S *)body->contents.binary, " for New Users");
  3613.  
  3614.     if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
  3615.       so_puts((STORE_S *)body->contents.binary, " and IMAP");
  3616.  
  3617.     if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
  3618.           && ps_global->VAR_NNTP_SERVER[0][0])
  3619.       so_puts((STORE_S *)body->contents.binary, " and NNTP");
  3620.  
  3621.     loser = pine_simple_send(outgoing, &body, NULL, NULL, 0);
  3622.  
  3623.     q_status_message2(SM_ORDER, 0, 3,"%sequest sent from \"%s\"",
  3624.               (loser) ? "No r" : "R",
  3625.               from_addr = addr_list_string(outgoing->from,NULL,1));
  3626.     fs_give((void **)&from_addr);
  3627.     }
  3628.     else
  3629.       q_status_message(SM_ORDER | SM_DING, 3, 4,
  3630.                "Problem creating space for message text.");
  3631.  
  3632.     mail_free_envelope(&outgoing);
  3633.     pine_free_body(&body);
  3634.  
  3635. }
  3636.  
  3637.  
  3638. /*----------------------------------------------------------------------
  3639.      Call the mailer, SMTP, sendmail or whatever
  3640.      
  3641. Args: header -- full header (envelope and local parts) of message to send
  3642.       body -- The full body of the message including text
  3643.       verbose -- flag to indicate verbose transaction mode requested
  3644.  
  3645. Returns: -1 if failed, 1 if succeeded
  3646. ----*/      
  3647. int
  3648. call_mailer(header, body)
  3649.     METAENV *header;
  3650.     BODY    *body;
  3651. {
  3652.     char         error_buf[100], *error_mess = NULL, *postcmd = NULL;
  3653.     ADDRESS     *a;
  3654.     ENVELOPE    *fake_env = NULL;
  3655.     int          addr_error_count, we_cancel = 0;
  3656.     long     smtp_opts;
  3657.     char    *verbose_file = NULL;
  3658.     void    *orig_getline, *orig_soutr, *orig_close, *orig_822_output;
  3659.     PIPE_S    *postpipe;
  3660.     PINEFIELD    *pf;
  3661.  
  3662. #define MAX_ADDR_ERROR 2  /* Only display 2 address errors */
  3663.  
  3664.     dprint(4, (debugfile, "Sending mail...\n"));
  3665.  
  3666.     /* Check for any recipients */
  3667.     for(pf = header->local; pf && pf->name; pf = pf->next)
  3668.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  3669.     break;
  3670.  
  3671.     if(!pf){
  3672.     q_status_message(SM_ORDER,3,3,
  3673.         "Can't send message. No recipients specified!");
  3674.     return(0);
  3675.     }
  3676.  
  3677.     /* set up counts and such to keep track sent percentage */
  3678.     send_bytes_sent = 0;
  3679.     gf_filter_init();                /* zero piped byte count, 'n */
  3680.     send_bytes_to_send = send_body_size(body);    /* count body bytes         */
  3681.     ps_global->c_client_error[0] = error_buf[0] = '\0';
  3682.     we_cancel = busy_alarm(1, "Sending mail",
  3683.                send_bytes_to_send ? sent_percent : NULL, 1);
  3684.  
  3685.     /* try posting via local "<mta> <-t>" if specified */
  3686.     if(mta_handoff(header, body, error_buf)){
  3687.     if(error_buf[0])
  3688.       error_mess = error_buf;
  3689.  
  3690.     goto done;
  3691.     }
  3692.  
  3693.     smtp_opts = (F_ON(F_ENABLE_8BIT, ps_global) && troll_8bit(body))
  3694.           ? SOP_ESMTP : 0L;
  3695. #ifdef    DEBUG
  3696.     if(debug > 5 || verbose_requested)
  3697.       smtp_opts |= SOP_DEBUG;
  3698. #endif
  3699.  
  3700.  
  3701.     /*
  3702.      * Set global header pointer so post_rfc822_output can get at it when
  3703.      * it's called back from c-client's sending routine...
  3704.      */
  3705.     send_header = header;
  3706.  
  3707.     /*
  3708.      * Fabricate a fake ENVELOPE to hand c-client's SMTP engine.
  3709.      * The purpose is to give smtp_mail the list for SMTP RCPT when
  3710.      * there are recipients in pine's METAENV that are outside c-client's
  3711.      * envelope.
  3712.      *  
  3713.      * NOTE: If there aren't any, don't bother.  Dealt with it below.
  3714.      */
  3715.     for(pf = header->local; pf && pf->name; pf = pf->next)
  3716.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr
  3717.      && !(*pf->addr == header->env->to || *pf->addr == header->env->cc
  3718.           || *pf->addr == header->env->bcc))
  3719.     break;
  3720.  
  3721.     if(pf && pf->name){
  3722.     ADDRESS **tail;
  3723.  
  3724.     fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
  3725.     memset(fake_env, 0, sizeof(ENVELOPE));
  3726.     fake_env->return_path = rfc822_cpy_adr(header->env->return_path);
  3727.     tail = &(fake_env->to);
  3728.     for(pf = header->local; pf && pf->name; pf = pf->next)
  3729.       if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
  3730.           *tail = rfc822_cpy_adr(*pf->addr);
  3731.           while(*tail)
  3732.         tail = &((*tail)->next);
  3733.       }
  3734.     }
  3735.  
  3736.     /*
  3737.      * Install our rfc822 output routine 
  3738.      */
  3739.     orig_822_output = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
  3740.     (void) mail_parameters(NULL, SET_RFC822OUTPUT, post_rfc822_output);
  3741.  
  3742.     /*
  3743.      * Allow for verbose posting
  3744.      */
  3745.     (void) mail_parameters(NULL, SET_POSTVERBOSE, pine_smtp_verbose_out);
  3746.  
  3747.     /*
  3748.      * OK, who posts what?  We tried an mta_handoff above, but there
  3749.      * was either none specified or we decided not to use it.  So,
  3750.      * if there's an smtp-server defined anywhere, 
  3751.      */
  3752.     if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
  3753.        && ps_global->VAR_SMTP_SERVER[0][0]){
  3754.     /*---------- SMTP ----------*/
  3755.     dprint(4, (debugfile, "call_mailer: via TCP\n"));
  3756.     ps_global->noshow_error = 1;
  3757.     TIME_STAMP("smtp-open start (tcp)", 1);
  3758.     sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
  3759.     ps_global->noshow_error = 0;
  3760.     }
  3761.     else if(postcmd = smtp_command(ps_global->c_client_error)){
  3762.     /*----- Send via LOCAL SMTP agent ------*/
  3763.     dprint(4, (debugfile, "call_mailer: via pipe\n"));
  3764.     orig_soutr = mail_parameters(NULL, GET_POSTSOUTR, NULL);
  3765.     orig_getline = mail_parameters(NULL, GET_POSTGETLINE, NULL);
  3766.     orig_close = mail_parameters(NULL, GET_POSTCLOSE, NULL);
  3767.     (void) mail_parameters(NULL, SET_POSTSOUTR, pine_pipe_soutr);
  3768.     (void) mail_parameters(NULL, SET_POSTGETLINE, pine_pipe_getline);
  3769.     (void) mail_parameters(NULL, SET_POSTCLOSE, pine_pipe_close);
  3770.  
  3771. /* BUG: should provide separate stderr output! */
  3772.     TIME_STAMP("smtp-open start (pipe)", 1);
  3773.     if(postpipe = open_system_pipe(postcmd, NULL, NULL,
  3774.          PIPE_READ | PIPE_STDERR | PIPE_WRITE | PIPE_PROT | PIPE_NOSHELL)){
  3775.         sending_stream = (SMTPSTREAM *) fs_get (sizeof (SMTPSTREAM));
  3776.         memset(sending_stream, 0, sizeof(SMTPSTREAM));
  3777.         sending_stream->tcpstream = (void *) postpipe;
  3778.         TIME_STAMP("smtp greeting (pipe)", 1);
  3779.         if(!smtp_greeting(sending_stream, "localhost", smtp_opts)){
  3780.         smtp_close(sending_stream);
  3781.         sending_stream = NULL;
  3782.         }
  3783.     }
  3784.     else{
  3785.         dprint(1, (debugfile, "Send via pipe failed!\n"));
  3786.         q_status_message(SM_ORDER | SM_DING, 4, 7, "Send Failed!");
  3787.     }
  3788.     }
  3789.  
  3790.     TIME_STAMP("smtp open", 1);
  3791.     if(sending_stream){
  3792.     dprint(1, (debugfile, "Opened SMTP server \"%s\"\n",
  3793.            postcmd ? postcmd : tcp_host(sending_stream->tcpstream)));
  3794.  
  3795.     if(verbose_requested){
  3796.         TIME_STAMP("verbose start", 1);
  3797.         if(verbose_file = temp_nam(NULL, "sd")){
  3798.         if(verbose_send_output = fopen(verbose_file, "w")){
  3799.             if(!pine_smtp_verbose(sending_stream))
  3800.               sprintf(error_mess = error_buf,
  3801.                   "Mail not sent.  VERBOSE mode error%s%.50s.",
  3802.                   (sending_stream && sending_stream->reply)
  3803.                     ? ": ": "",
  3804.                   (sending_stream && sending_stream->reply)
  3805.                     ? sending_stream->reply : "");
  3806.         }
  3807.         else
  3808.           strcpy(error_mess = error_buf,
  3809.              "Can't open tmp file for VERBOSE mode.");
  3810.         }
  3811.         else
  3812.           strcpy(error_mess = error_buf,
  3813.              "Can't create tmp file name for VERBOSE mode.");
  3814.  
  3815.         TIME_STAMP("verbose end", 1);
  3816.     }
  3817.  
  3818.     TIME_STAMP("smtp start", 1);
  3819.     if(!error_mess && !smtp_mail(sending_stream, "MAIL",
  3820.                      fake_env ? fake_env : header->env, body)){
  3821.         sprintf(error_buf,
  3822.             "Mail not sent. Sending error%s%.40s",
  3823.             (sending_stream && sending_stream->reply) ? ": ": ".",
  3824.             (sending_stream && sending_stream->reply)
  3825.               ? sending_stream->reply : "");
  3826.         dprint(1, (debugfile, error_buf));
  3827.         addr_error_count = 0;
  3828.         for(pf = header->local; pf && pf->name; pf = pf->next)
  3829.           if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
  3830.         for(a = *pf->addr; a != NULL; a = a->next)
  3831.           if(a->error != NULL){
  3832.               if(addr_error_count++ < MAX_ADDR_ERROR){
  3833.               if(error_mess)
  3834.                 q_status_message(SM_ORDER, 4, 7, error_mess);
  3835.  
  3836.               error_mess = tidy_smtp_mess(a->error,
  3837.                               "Mail not sent: %.60s",
  3838.                               error_buf);
  3839.               }
  3840.  
  3841.               dprint(1, (debugfile, "Send Error: \"%s\"\n",
  3842.                  a->error));
  3843.           }
  3844.  
  3845.         if(!error_mess)
  3846.           error_mess = error_buf;
  3847.     }
  3848.  
  3849.     TIME_STAMP("smtp closing", 1);
  3850.     smtp_close(sending_stream);
  3851.     sending_stream = NULL;
  3852.     TIME_STAMP("smtp done", 1);
  3853.     }
  3854.     else if(!error_mess)
  3855.       sprintf(error_mess = error_buf, "Error sending: %.60s",
  3856.           ps_global->c_client_error);
  3857.  
  3858.     if(verbose_file){
  3859.     if(verbose_send_output){
  3860.         TIME_STAMP("verbose start", 1);
  3861.         fclose(verbose_send_output);
  3862.         verbose_send_output = NULL;
  3863.         q_status_message(SM_ORDER, 0, 3, "Verbose SMTP output received");
  3864.         display_output_file(verbose_file, "Verbose SMTP Interaction",NULL);
  3865.         TIME_STAMP("verbose end", 1);
  3866.     }
  3867.  
  3868.     fs_give((void **)&verbose_file);
  3869.     }
  3870.  
  3871.     /*
  3872.      * Restore original 822 emitter and possibly I/O routines
  3873.      */
  3874.     (void) mail_parameters(NULL, SET_RFC822OUTPUT, orig_822_output);
  3875.     if(postcmd){
  3876.     fs_give((void **)&postcmd);
  3877.     (void) mail_parameters(NULL, SET_POSTSOUTR, orig_soutr);
  3878.     (void) mail_parameters(NULL, SET_POSTGETLINE, orig_getline);
  3879.     (void) mail_parameters(NULL, SET_POSTCLOSE, orig_close);
  3880.     }
  3881.  
  3882.     if(fake_env)
  3883.       mail_free_envelope(&fake_env);
  3884.  
  3885.   done:
  3886.     if(we_cancel)
  3887.       cancel_busy_alarm(0);
  3888.  
  3889.     TIME_STAMP("call_mailer done", 1);
  3890.     /*-------- Did message make it ? ----------*/
  3891.     if(error_mess){
  3892.         /*---- Error sending mail -----*/
  3893.     if(local_so && !local_written)
  3894.       so_give(&local_so);
  3895.  
  3896.         q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess);
  3897.     dprint(1, (debugfile, "call_mailer ERROR: %s\n", error_mess));
  3898.     return(-1);
  3899.     }
  3900.     else{
  3901.     local_written = 1;
  3902.     return(1);
  3903.     }
  3904. }
  3905.  
  3906.  
  3907. /*----------------------------------------------------------------------
  3908.     Checks to make sure the fcc is available and can be opened
  3909.  
  3910. Args: fcc -- the name of the fcc to create.  It can't be NULL.
  3911.       fcc_cntxt -- Returns the context the fcc is in.
  3912.       force -- supress user option prompt
  3913.  
  3914. Returns 0 on failure, 1 on success
  3915.   ----*/
  3916. open_fcc(fcc, fcc_cntxt, force)
  3917.     char       *fcc;
  3918.     CONTEXT_S **fcc_cntxt;
  3919.     int      force;
  3920. {
  3921.     MAILSTREAM *create_stream;
  3922.     int        ok = 1;
  3923.  
  3924.     *fcc_cntxt = NULL;
  3925.  
  3926.     /* 
  3927.      * check for fcc's existance...
  3928.      */
  3929.     TIME_STAMP("open_fcc start", 1);
  3930.     if(context_isambig(fcc)){
  3931.  
  3932.     /*
  3933.      * We only want to set the "context" if fcc is an ambiguous
  3934.      * name.  Otherwise, our "relativeness" rules for contexts 
  3935.      * (implemented in context.c) might cause the name to be
  3936.      * interpreted in the wrong context...
  3937.      */
  3938.     if(!(*fcc_cntxt = default_save_context(ps_global->context_list)))
  3939.       *fcc_cntxt = ps_global->context_list;
  3940.  
  3941.         find_folders_in_context(NULL, *fcc_cntxt, fcc);
  3942.         if(folder_index(fcc, (*fcc_cntxt)->folders) < 0){
  3943.         if(ps_global->context_list->next){
  3944.         sprintf(tmp_20k_buf,
  3945.             "Folder \"%.20s\" in <%.30s> doesn't exist. Create",
  3946.             fcc, (*fcc_cntxt)->label[0]);
  3947.         }
  3948.         else
  3949.           sprintf(tmp_20k_buf,"Folder \"%.40s\" doesn't exist.  Create",
  3950.               fcc);
  3951.  
  3952.         if(!force && want_to(tmp_20k_buf, 'y', 'n', NO_HELP, 0, 0) != 'y'){
  3953.         q_status_message(SM_ORDER | SM_DING, 3, 4,
  3954.                  "Fcc of message rejected");
  3955.         ok = 0;
  3956.         }
  3957.         else{
  3958.         /*
  3959.          * See if an already open stream will service the create
  3960.          */
  3961.         create_stream = context_same_stream((*fcc_cntxt)->context,
  3962.                             fcc,
  3963.                             ps_global->mail_stream);
  3964.         if(!create_stream
  3965.            && ps_global->mail_stream != ps_global->inbox_stream)
  3966.           create_stream = context_same_stream((*fcc_cntxt)->context,
  3967.                               fcc,
  3968.                               ps_global->inbox_stream);
  3969.  
  3970.         if(!context_create((*fcc_cntxt)->context, create_stream, fcc))
  3971.           ok = 0;
  3972.         }
  3973.         }
  3974.  
  3975.         free_folders_in_context(*fcc_cntxt);
  3976.         if(!ok){
  3977.         TIME_STAMP("open_fcc done.", 1);
  3978.         return(0);
  3979.     }
  3980.     }
  3981.     else if((ok = folder_exists(NULL, fcc)) <= 0){
  3982.         sprintf(tmp_20k_buf,"Folder \"%.40s\" doesn't exist.  Create",fcc);
  3983.  
  3984.         if(ok < 0
  3985.        || (!force && want_to(tmp_20k_buf, 'y', 'n', NO_HELP,0,0) != 'y')){
  3986.         q_status_message(SM_ORDER | SM_DING, 3, 3,
  3987.                  "Fcc of message rejected");
  3988.         TIME_STAMP("open_fcc done.", 1);
  3989.         return(0);
  3990.     }
  3991.  
  3992.     /*
  3993.      * See if an already open stream will service the create
  3994.      */
  3995.     create_stream = same_stream(fcc, ps_global->mail_stream);
  3996.     if(!create_stream && ps_global->mail_stream != ps_global->inbox_stream)
  3997.       create_stream = same_stream(fcc, ps_global->inbox_stream);
  3998.  
  3999.         if(!mail_create(create_stream, fcc)){
  4000.         TIME_STAMP("open_fcc done.", 1);
  4001.         return(0);
  4002.     }
  4003.     }
  4004.  
  4005.     TIME_STAMP("open_fcc done.", 1);
  4006.     return(1);
  4007. }
  4008.  
  4009.  
  4010. /*----------------------------------------------------------------------
  4011.    mail_append() the fcc accumulated in temp_storage to proper destination
  4012.  
  4013. Args:  fcc -- name of folder
  4014.        fcc_cntxt -- context for folder
  4015.        temp_storage -- String of file where Fcc has been accumulated
  4016.  
  4017. This copies the string of file to the actual folder, which might be IMAP
  4018. or a disk folder.  The temp_storage is freed after it is written.
  4019. An error message is produced if this fails.
  4020.   ----*/
  4021. int
  4022. write_fcc(fcc, fcc_cntxt, tmp_storage, label)
  4023.      char      *fcc;
  4024.      CONTEXT_S *fcc_cntxt;
  4025.      STORE_S   *tmp_storage;
  4026.      char      *label;
  4027. {
  4028.     STRING      msg;
  4029.     MAILSTREAM *fcc_stream;
  4030.     char       *cntxt_string;
  4031.     int         we_cancel = 0;
  4032. #ifdef    DOS
  4033.     struct {            /* hack! stolen from dawz.c */
  4034.     int fd;
  4035.     unsigned long pos;
  4036.     } d;
  4037.     extern STRINGDRIVER dawz_string;
  4038. #endif
  4039.  
  4040.     if(!tmp_storage)
  4041.       return(0);
  4042.  
  4043.     TIME_STAMP("write_fcc start.", 1);
  4044.     dprint(4, (debugfile, "Writing %s\n", (label && *label) ? label : ""));
  4045.     if(label && *label){
  4046.     char msg_buf[80];
  4047.  
  4048.     strncat(strcpy(msg_buf, "Writing "), label, 70);
  4049.     we_cancel = busy_alarm(1, msg_buf, NULL, 1);
  4050.     }
  4051.     else
  4052.       we_cancel = busy_alarm(1, NULL, NULL, 0);
  4053.  
  4054.     so_seek(tmp_storage, 0L, 0);
  4055. #ifdef    DOS
  4056.     d.fd  = fileno((FILE *)so_text(tmp_storage));
  4057.     d.pos = 0L;
  4058.     INIT(&msg, dawz_string, (void *)&d, filelength(d.fd));
  4059. #else
  4060.     INIT(&msg, mail_string, (void *)so_text(tmp_storage), 
  4061.          strlen((char *)so_text(tmp_storage)));
  4062. #endif
  4063.  
  4064.     cntxt_string = fcc_cntxt ? fcc_cntxt->context : "[]";
  4065.     fcc_stream   = context_same_stream(cntxt_string, fcc,
  4066.                        ps_global->mail_stream);
  4067.  
  4068.     if(!fcc_stream && ps_global->mail_stream != ps_global->inbox_stream)
  4069.       fcc_stream = context_same_stream(cntxt_string, fcc,
  4070.                        ps_global->inbox_stream);
  4071.  
  4072.     if(!context_append(cntxt_string, fcc_stream, fcc, &msg)){
  4073.     if(we_cancel)
  4074.       cancel_busy_alarm(-1);
  4075.  
  4076.     q_status_message1(SM_ORDER | SM_DING, 3, 5,
  4077.               "Write to \"%s\" FAILED!!!", fcc);
  4078.     dprint(1, (debugfile, "ERROR appending %s in \"%s\"",
  4079.            fcc, cntxt_string));
  4080.     return(0);
  4081.     }
  4082.  
  4083.     if(we_cancel)
  4084.       cancel_busy_alarm(label ? 0 : -1);
  4085.  
  4086.     dprint(4, (debugfile, "done.\n"));
  4087.     TIME_STAMP("write_fcc done.", 1);
  4088.     return(1);
  4089. }
  4090.  
  4091.   
  4092.  
  4093. /*
  4094.  * troll_8bit - return TRUE if somewhere in the body 8BIT data's
  4095.  *        contained.
  4096.  */
  4097. int
  4098. troll_8bit(body)
  4099.     BODY *body;
  4100. {
  4101.     PART *part;
  4102.  
  4103.     if (body)
  4104.       switch (body->type) {
  4105.     case TYPEMULTIPART:        /* multi-part */
  4106.       part = body->contents.part;    /* sniff body parts */
  4107.       if(troll_8bit(&part->body))
  4108.         return(TRUE);
  4109.       while (part = part->next);    /* until done */
  4110.       break;
  4111.  
  4112.     default:            /* bingo, 8BIT segment! */
  4113.       if(body->encoding == ENC8BIT)
  4114.         return(TRUE);
  4115.  
  4116.       break;
  4117.       }
  4118.  
  4119.     return(FALSE);
  4120. }
  4121.  
  4122.  
  4123.  
  4124. /*----------------------------------------------------------------------
  4125.     Remove the leading digits from SMTP error messages
  4126.  -----*/
  4127. char *
  4128. tidy_smtp_mess(error, printstring, outbuf)
  4129.     char *error, *printstring, *outbuf;
  4130. {
  4131.     while(isdigit(*error) || isspace(*error))
  4132.       error++;
  4133.  
  4134.     sprintf(outbuf, printstring, error);
  4135.     return(outbuf);
  4136. }
  4137.  
  4138.         
  4139.     
  4140. /*----------------------------------------------------------------------
  4141.     Set up fields for passing to pico.  Assumes first text part is
  4142.     intended to be passed along for editing, and is in the form of
  4143.     of a storage object brought into existence sometime before pico_send().
  4144.  -----*/
  4145. void
  4146. outgoing2strings(header, bod, text, pico_a)
  4147.     METAENV   *header;
  4148.     BODY      *bod;
  4149.     void     **text;
  4150.     PATMT    **pico_a;
  4151. {
  4152.     PART      *part;
  4153.     PATMT     *pa;
  4154.     char      *type;
  4155.     PINEFIELD *pf;
  4156.     PARAMETER *parms;
  4157.  
  4158.     /*
  4159.      * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
  4160.      * is guaranteed to be of type PicoText!
  4161.      */
  4162.     if(bod->type == TYPETEXT){
  4163.     *text = so_text(bod->contents.binary);
  4164.     } else if(bod->type == TYPEMULTIPART){
  4165.     /*
  4166.      * We used to jump out the window if the first part wasn't text,
  4167.      * but that may not be the case when bouncing a message with
  4168.      * a leading non-text segment.  So, IT'S UNDERSTOOD that the 
  4169.      * contents of the first part to send is still ALWAYS in a 
  4170.      * PicoText storage object, *AND* if that object doesn't contain
  4171.      * data of type text, then it must contain THE ENCODED NON-TEXT
  4172.      * DATA of the piece being sent.
  4173.      *
  4174.      * It's up to the programmer to make sure that such a message is
  4175.      * sent via pine_simple_send and never get to the composer via
  4176.      * pine_send.
  4177.      *
  4178.      * Make sense?
  4179.      */
  4180.     *text = so_text(bod->contents.part->body.contents.binary);
  4181.  
  4182.     /*
  4183.      * If we already had a list, blast it now, so we can build a new
  4184.      * attachment list that reflects what's really there...
  4185.      */
  4186.     if(pico_a)
  4187.       free_attachment_list(pico_a);
  4188.  
  4189.  
  4190.         /* Simplifyihg assumption #28e. (see cross reference) 
  4191.            All parts in the body passed in here that are not already
  4192.            in the attachments list are added to the end of the attachments
  4193.            list. Attachment items not in the body list will be taken care
  4194.            of in strings2outgoing, but they are unlikey to occur
  4195.          */
  4196.  
  4197.         for(part = bod->contents.part->next; part != NULL; part = part->next) {
  4198.             for(pa = *pico_a; pa != NULL; pa = pa->next) {
  4199.                 if(strcmp(pa->id, part->body.id) == 0)
  4200.                   break;  /* Already in list */
  4201.             }
  4202.             if(pa != NULL) /* Was in the list, don't worry 'bout this part */
  4203.               continue;
  4204.  
  4205.             /* to end of list */
  4206.             for(pa = *pico_a;  pa!= NULL && pa->next != NULL;  pa = pa->next);
  4207.             /* empty list or no? */
  4208.             if(pa == NULL) {
  4209.                 /* empty list */
  4210.                 *pico_a = (PATMT *)fs_get(sizeof(PATMT));
  4211.                 pa = *pico_a;
  4212.             } else {
  4213.                 pa->next = (PATMT *)fs_get(sizeof(PATMT));
  4214.                 pa = pa->next;
  4215.             }
  4216.             pa->description = part->body.description == NULL ? cpystr("") : 
  4217.                                               cpystr(part->body.description);
  4218.             
  4219.             type = type_desc(part->body.type,
  4220.                  part->body.subtype,part->body.parameter,0);
  4221.  
  4222.         /*
  4223.          * If we can find a "name" parm, display that too...
  4224.          */
  4225.         for(parms = part->body.parameter; parms; parms = parms->next)
  4226.           if(!strucmp(parms->attribute, "name") && parms->value)
  4227.         break;
  4228.  
  4229.             pa->filename = fs_get(strlen(type)
  4230.                   + (parms ? strlen(parms->value) : 0) + 5);
  4231.  
  4232.             sprintf(pa->filename, "[%s%s%s]", type,
  4233.             parms ? ": " : "", 
  4234.             parms ? parms->value : "");
  4235.             pa->flags    = A_FLIT;
  4236.             pa->size     = cpystr(byte_string(part->body.size.bytes));
  4237.             if(part->body.id == NULL)
  4238.               part->body.id = generate_message_id(ps_global);
  4239.             pa->id       = cpystr(part->body.id);
  4240.             pa->next     = NULL;
  4241.         }
  4242.     }
  4243.         
  4244.  
  4245.     /*------------------------------------------------------------------
  4246.        Malloc strings to pass to composer editor because it expects
  4247.        such strings so it can realloc them
  4248.       -----------------------------------------------------------------*/
  4249.     /*
  4250.      * turn any address fields into text strings
  4251.      */
  4252.     /*
  4253.      * SIMPLIFYING ASSUMPTION #116: all header strings are understood
  4254.      * NOT to be RFC1522 decoded.  Said differently, they're understood
  4255.      * to be RFC1522 ENCODED as necessary.  The intent is to preserve
  4256.      * original charset tagging as far into the compose/send pipe as
  4257.      * we can.
  4258.      */
  4259.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4260.       if(pf->canedit)
  4261.     switch(pf->type){
  4262.       case Address :
  4263.         if(pf->addr){
  4264.         char *p, *t, *u;
  4265.         long  l;
  4266.  
  4267.         pf->scratch = addr_list_string(*pf->addr, NULL, 0);
  4268.  
  4269.         /*
  4270.          * Scan for and fix-up patently bogus fields.
  4271.          *
  4272.          * NOTE: collaboration with this code and what's done in
  4273.          * reply.c:reply_cp_addr to package up the bogus stuff
  4274.          * is required.
  4275.          */
  4276.         for(p = pf->scratch; p = strstr(p, "@.RAW-FIELD."); )
  4277.           for(t = p; ; t--)
  4278.             if(*t == '&'){        /* find "leading" token */
  4279.             *t++ = ' ';        /* replace token */
  4280.             *p = '\0';        /* tie off string */
  4281.             u = rfc822_base64((unsigned char *) t,
  4282.                       (unsigned long) strlen(t),
  4283.                       (unsigned long *) &l);
  4284.             *p = '@';        /* restore 'p' */
  4285.             rplstr(p, 12, "");    /* clear special token */
  4286.             rplstr(t, strlen(t), u);
  4287.             fs_give((void **) &u);
  4288.             if(pf->he)
  4289.               pf->he->start_here = 1;
  4290.  
  4291.             break;
  4292.             }
  4293.             else if(t == pf->scratch)
  4294.               break;
  4295.         }
  4296.  
  4297.         break;
  4298.  
  4299.       case Subject :
  4300.         if(pf->text){
  4301.         if(pf->scratch){
  4302.             char *p, *charset = NULL;
  4303.  
  4304.             p = (char *)fs_get((strlen(pf->scratch)+1)*sizeof(char));
  4305.             if(rfc1522_decode((unsigned char *)p, pf->scratch,&charset)
  4306.                                == (unsigned char *) p){
  4307.             fs_give((void **)&pf->scratch);
  4308.             pf->scratch = p;
  4309.             }
  4310.             else
  4311.               fs_give((void **)&p);
  4312.  
  4313.             if(charset)
  4314.               fs_give((void **)&charset);
  4315.         }
  4316.         else
  4317.           pf->scratch = cpystr((*pf->text) ? *pf->text : "");
  4318.         }
  4319.  
  4320.         break;
  4321.     }
  4322. }
  4323.  
  4324.  
  4325. /*----------------------------------------------------------------------
  4326.     Restore fields returned from pico to form useful to sending
  4327.     routines.
  4328.  -----*/
  4329. void
  4330. strings2outgoing(header, bod, attach)
  4331.     METAENV  *header;
  4332.     BODY    **bod;
  4333.     PATMT    *attach;
  4334. {
  4335.     PINEFIELD *pf;
  4336.     int we_cancel = 0;
  4337.  
  4338.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  4339.  
  4340.     /*
  4341.      * turn any local address strings into address lists
  4342.      */
  4343.     for(pf = header->local; pf && pf->name; pf = pf->next)
  4344.       if(pf->scratch){
  4345.       if(pf->canedit && (!pf->he || pf->he->dirty)){
  4346.           switch(pf->type){
  4347.         case Address :
  4348.           removing_trailing_white_space(pf->scratch);
  4349.           if(*pf->scratch){
  4350.               ADDRESS     *new_addr = NULL;
  4351.               static char *fakedomain = "@";
  4352.  
  4353.               rfc822_parse_adrlist(&new_addr, pf->scratch,
  4354.                    (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
  4355.                      ? fakedomain : ps_global->maildomain);
  4356.               resolve_encoded_entries(new_addr, *pf->addr);
  4357.               mail_free_address(pf->addr);    /* free old addrs */
  4358.               *pf->addr = new_addr;        /* assign new addr */
  4359.           }
  4360.           else
  4361.             mail_free_address(pf->addr);    /* free old addrs */
  4362.  
  4363.           break;
  4364.  
  4365.         case Subject :
  4366.           if(*pf->text)
  4367.             fs_give((void **)pf->text);
  4368.  
  4369.           *pf->text = cpystr(pf->scratch);
  4370.           break;
  4371.           }
  4372.       }
  4373.  
  4374.       fs_give((void **)&pf->scratch);    /* free now useless text */
  4375.       }
  4376.  
  4377.     create_message_body(bod, attach);
  4378.     pine_encode_body(*bod);
  4379.  
  4380.     if(we_cancel)
  4381.       cancel_busy_alarm(-1);
  4382. }
  4383.  
  4384.  
  4385. /*----------------------------------------------------------------------
  4386.  -----*/
  4387. void
  4388. resolve_encoded_entries(new, old)
  4389.     ADDRESS *new, *old;
  4390. {
  4391.     ADDRESS *a;
  4392.  
  4393.     /* BUG: deal properly with group syntax? */
  4394.     for(; old; old = old->next)
  4395.       if(old->personal && old->mailbox && old->host)
  4396.     for(a = new; a; a = a->next)
  4397.       if(a->personal && a->mailbox && !strcmp(old->mailbox, a->mailbox)
  4398.          && a->host && !strcmp(old->host, a->host)){
  4399.           char *charset = NULL;
  4400.  
  4401.           if(strcmp(a->personal,
  4402.             (char *) rfc1522_decode((unsigned char *)tmp_20k_buf,
  4403.                         old->personal, &charset))){
  4404.           fs_give((void **)&a->personal);
  4405.           a->personal = cpystr(old->personal);
  4406.           }
  4407.  
  4408.           if(charset)
  4409.         fs_give((void **)&charset);
  4410.  
  4411.           break;
  4412.       }
  4413. }
  4414.  
  4415.  
  4416. /*----------------------------------------------------------------------
  4417.  
  4418.  The head of the body list here is always either TEXT or MULTIPART. It may be
  4419. changed from TEXT to MULTIPART if there are attachments to be added
  4420. and it is not already multipart. 
  4421.   ----*/
  4422. void
  4423. create_message_body(b, attach)
  4424.     BODY  **b;
  4425.     PATMT  *attach;
  4426. {
  4427.     PART         *p, *p_trail;
  4428.     PATMT        *pa;
  4429.     BODY         *b1;
  4430.     void         *file_contents;
  4431.     PARAMETER    *pm;
  4432.     char         *lc;
  4433.  
  4434.     TIME_STAMP("create_body start.", 1);
  4435.     if((*b)->type != TYPEMULTIPART && !attach){
  4436.     /* only override assigned encoding if it might need upgrading */
  4437.     if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
  4438.       (*b)->encoding = ENCOTHER;
  4439.  
  4440.     set_mime_type_by_grope(*b);
  4441.     set_body_size(*b);
  4442.     TIME_STAMP("create_body end.", 1);
  4443.         return;
  4444.     }
  4445.  
  4446.     if((*b)->type == TYPETEXT) {
  4447.         /*-- Current type is text, but there are attachments to add --*/
  4448.         /*-- Upgrade to a TYPEMULTIPART --*/
  4449.         b1                                  = (BODY *)mail_newbody();
  4450.         b1->type                            = TYPEMULTIPART;
  4451.         b1->contents.part                   = mail_newbody_part();
  4452.         b1->contents.part->body             = **b;
  4453.  
  4454.         (*b)->subtype = (*b)->id = (*b)->description = NULL;
  4455.     (*b)->parameter = NULL;
  4456.     (*b)->contents.binary               = NULL;
  4457.     pine_free_body(b);
  4458.         *b = b1;
  4459.     }
  4460.  
  4461.     /*-- Now type must be MULTIPART with first part text --*/
  4462.     (*b)->contents.part->body.encoding = ENCOTHER;
  4463.     set_mime_type_by_grope(&((*b)->contents.part->body));
  4464.     set_body_size(&((*b)->contents.part->body));
  4465.  
  4466.     /*------ Go through the parts list remove those to be deleted -----*/
  4467.     for(p = p_trail = (*b)->contents.part->next; p != NULL;) {
  4468.     for(pa = attach; pa && p->body.id; pa = pa->next)
  4469.       if(pa->id && strcmp(pa->id, p->body.id) == 0){ /* already existed */
  4470.           if(!p->body.description        /* update description? */
  4471.          || strcmp(pa->description, p->body.description)){
  4472.           if(p->body.description)
  4473.             fs_give((void **)&p->body.description);
  4474.  
  4475.           p->body.description = bitstrip(cpystr(pa->description));
  4476.           }
  4477.  
  4478.           break;
  4479.       }
  4480.  
  4481.         if(pa == NULL) {
  4482.             /* attachment wasn't in the list; zap it */
  4483.             if(p == (*b)->contents.part->next) {
  4484.                 /* Beginning of list */
  4485.                 (*b)->contents.part->next = p->next;
  4486.                 p->next = NULL;  /* Don't free the whole chain */
  4487.                 pine_free_body_part(&p);
  4488.                 p = p_trail = (*b)->contents.part->next;
  4489.             } else {
  4490.                 p_trail->next = p->next;
  4491.                 p->next = NULL;  /* Don't free the whole chain */
  4492.                 pine_free_body_part(&p);
  4493.                 p = p_trail->next;
  4494.             }
  4495.         } else {
  4496.             p_trail = p;
  4497.             p       = p->next;
  4498.         }
  4499.     }
  4500.  
  4501.     /*---------- Now add any new attachments ---------*/
  4502.     for(p = (*b)->contents.part ; p->next != NULL; p = p->next);
  4503.     for(pa = attach; pa != NULL; pa = pa->next) {
  4504.         if(pa->id != NULL)
  4505.       continue;            /* Has an ID, it's old */
  4506.  
  4507.     /*
  4508.      * the idea is handle ALL attachments as open FILE *'s.  Actual
  4509.          * encoding and such is handled at the time the message
  4510.          * is shoved into the mail slot or written to disk...
  4511.      *
  4512.          * Also, we never unlink a file, so it's up to whoever opens
  4513.          * it to deal with tmpfile issues.
  4514.      */
  4515.     if((file_contents = (void *)so_get(FileStar, pa->filename,
  4516.                        READ_ACCESS)) == NULL){
  4517.             q_status_message2(SM_ORDER | SM_DING, 3, 4,
  4518.                               "Error \"%s\", couldn't attach file \"%s\"",
  4519.                               error_description(errno), pa->filename);
  4520.             display_message('x');
  4521.             continue;
  4522.         }
  4523.         
  4524.         p->next                      = mail_newbody_part();
  4525.         p                            = p->next;
  4526.         p->body.id                   = generate_message_id(ps_global);
  4527.         p->body.contents.binary      = file_contents;
  4528.  
  4529.     /*
  4530.      * Set type to unknown and let set_mime_type_by_* figure it out.
  4531.      * Always encode attachments we add as BINARY.
  4532.      */
  4533.     p->body.type             = TYPEOTHER;
  4534.     p->body.encoding         = ENCBINARY;
  4535.     p->body.size.bytes           = name_file_size(pa->filename);
  4536.     if(!set_mime_type_by_extension(&p->body, pa->filename))
  4537.       set_mime_type_by_grope(&p->body);
  4538.  
  4539.     so_release((STORE_S *)p->body.contents.binary);
  4540.         p->body.description          = bitstrip(cpystr(pa->description));
  4541.     /* add name attribute */
  4542.     if (p->body.parameter == NULL) {
  4543.         pm = p->body.parameter = mail_newbody_parameter();
  4544.         pm->attribute = cpystr("name");
  4545.     }else {
  4546.             for (pm = p->body.parameter;
  4547.             strucmp(pm->attribute, "name") && pm->next != NULL;
  4548.                                 pm = pm->next);
  4549.         if (strucmp(pm->attribute, "name") != 0) {
  4550.         pm->next = mail_newbody_parameter();
  4551.         pm = pm->next;
  4552.         pm->attribute = cpystr("name");
  4553.         }
  4554.     }
  4555.  
  4556.     pm->value = bitstrip(cpystr((lc = last_cmpnt(pa->filename))
  4557.                       ? lc : pa->filename));
  4558.  
  4559.         p->next = NULL;
  4560.         pa->id = cpystr(p->body.id);
  4561.     }
  4562.  
  4563.     TIME_STAMP("create_body end.", 1);
  4564. }
  4565.  
  4566.  
  4567. /*
  4568.  * Build and return the "From:" address for outbound messages from
  4569.  * global data...
  4570.  */
  4571. ADDRESS *
  4572. generate_from()
  4573. {
  4574.     ADDRESS *addr = mail_newaddr();
  4575.     if(ps_global->VAR_PERSONAL_NAME)
  4576.       addr->personal = cpystr(ps_global->VAR_PERSONAL_NAME);
  4577.  
  4578.     addr->mailbox = cpystr(ps_global->VAR_USER_ID);
  4579.     addr->host    = cpystr(ps_global->maildomain);
  4580.     return(addr);
  4581. }
  4582.  
  4583.  
  4584. /*
  4585.  * free_attachment_list - free attachments in given list
  4586.  */
  4587. void
  4588. free_attachment_list(alist)
  4589.     PATMT  **alist;
  4590. {
  4591.     PATMT  *leading;
  4592.  
  4593.     while(alist && *alist){        /* pointer pointing to something */
  4594.     leading = (*alist)->next;
  4595.     if((*alist)->description)
  4596.           fs_give((void **)&(*alist)->description);
  4597.  
  4598.     if((*alist)->filename){
  4599.         if((*alist)->flags & A_TMP)
  4600.           unlink((*alist)->filename);
  4601.  
  4602.         fs_give((void **)&(*alist)->filename);
  4603.     }
  4604.  
  4605.     if((*alist)->size)
  4606.           fs_give((void **)&(*alist)->size);
  4607.  
  4608.     if((*alist)->id)
  4609.           fs_give((void **)&(*alist)->id);
  4610.  
  4611.     fs_give((void **)alist);
  4612.  
  4613.     *alist = leading;
  4614.     }
  4615. }
  4616.  
  4617.  
  4618.  
  4619. /*----------------------------------------------------------------------
  4620.   Insert the addition into the message id before first "@"
  4621.  
  4622.  This may be called twice on the same message-ID, but it won't do anything
  4623.  the second time. This will cause the status in the message-ID to be wrong
  4624.  if an attempt to send the message is made, an error occurs, and then the
  4625.  size or MIME parts changed and it is sent again.
  4626.   ----*/
  4627. void 
  4628. update_message_id(e, addition)
  4629.     ENVELOPE *e;
  4630.     char *addition;
  4631. {
  4632.     char *p, *q, *r, *new;
  4633.  
  4634.     new = fs_get(strlen(e->message_id) + strlen(addition) + 5);
  4635.     for(p = new, q = e->message_id; *q && *q != '@'; *p++ = *q++);
  4636.     if(p > new && isdigit(*(p-1))) {
  4637.     /* Already been updated if it's a digit, not a letter */
  4638.     fs_give((void **)&new);
  4639.     return;
  4640.     }
  4641.  
  4642.     *p++ = '-';
  4643.     for(r = addition; *r ; *p++ = *r++);
  4644.     for(; *q; *p++ = *q++);
  4645.     *p = *q;
  4646.     fs_give((void **)&(e->message_id));
  4647.     e->message_id = new;
  4648. }
  4649.  
  4650.  
  4651.  
  4652.  
  4653. static struct mime_count {
  4654.     int text_parts;
  4655.     int image_parts;
  4656.     int message_parts;
  4657.     int application_parts;
  4658.     int audio_parts;
  4659.     int  video_parts;
  4660. } mc;
  4661.  
  4662. char *
  4663. mime_stats(body)
  4664.     BODY *body;
  4665. {
  4666.     static char id[10];
  4667.     mc.text_parts = 0;
  4668.     mc.image_parts = 0;
  4669.     mc.message_parts = 0;
  4670.     mc.application_parts = 0;
  4671.     mc.audio_parts = 0;
  4672.     mc.video_parts = 0;
  4673.  
  4674.     mime_recur(body);
  4675.  
  4676.     mc.text_parts        = min(8, mc.text_parts );
  4677.     mc.image_parts       = min(8, mc.image_parts );
  4678.     mc.message_parts     = min(8, mc.message_parts );
  4679.     mc.application_parts = min(8, mc.application_parts );
  4680.     mc.audio_parts       = min(8, mc.audio_parts );
  4681.     mc.video_parts       = min(8, mc.video_parts );
  4682.  
  4683.  
  4684.     id[0] = encode_bits(mc.text_parts);
  4685.     id[1] = encode_bits(mc.message_parts);
  4686.     id[2] = encode_bits(mc.application_parts);
  4687.     id[3] = encode_bits(mc.video_parts);
  4688.     id[4] = encode_bits(mc.audio_parts);
  4689.     id[5] = encode_bits(mc.image_parts);
  4690.     id[6] = '\0';
  4691.     return(id);
  4692. }
  4693.     
  4694.  
  4695.  
  4696. /*----------------------------------------------------------------------
  4697.    ----*/
  4698. void
  4699. mime_recur(body)
  4700.     BODY *body;
  4701. {
  4702.     PART *part;
  4703.     switch (body->type) {
  4704.       case TYPETEXT:
  4705.         mc.text_parts++;
  4706.         break;
  4707.       case TYPEIMAGE:
  4708.         mc.image_parts++;
  4709.         break;
  4710.       case TYPEMESSAGE:
  4711.         mc.message_parts++;
  4712.         break;
  4713.       case TYPEAUDIO:
  4714.         mc.audio_parts++;
  4715.         break;
  4716.       case TYPEAPPLICATION:
  4717.         mc.application_parts++;
  4718.         break;
  4719.       case TYPEVIDEO:
  4720.         mc.video_parts++;
  4721.         break;
  4722.       case TYPEMULTIPART:
  4723.         for(part = body->contents.part; part != NULL; part = part->next) 
  4724.           mime_recur(&(part->body));
  4725.         break;
  4726.     }
  4727. }
  4728.         
  4729. int        
  4730. encode_bits(bits)
  4731.     int bits;
  4732. {
  4733.     if(bits < 10)
  4734.       return(bits + '0');
  4735.     else if(bits < 36)
  4736.       return(bits - 10 + 'a');
  4737.     else if (bits < 62)
  4738.       return(bits - 36 + 'A');
  4739.     else
  4740.       return('.');
  4741. }
  4742.  
  4743.  
  4744. /*
  4745.  * set_mime_type_by_grope - sniff the given storage object to determine its 
  4746.  *                  type, subtype and encoding
  4747.  *
  4748.  *        "Type" and "encoding" must be set before calling this routine.
  4749.  *        If "type" is set to something other than TYPEOTHER on entry,
  4750.  *        then that is the "type" we wish to use.  Same for "encoding"
  4751.  *        using ENCOTHER instead of TYPEOTHER.  Otherwise, we
  4752.  *        figure them out here.  If "type" is already set, we also
  4753.  *        leave subtype alone.  If not, we figure out subtype here.
  4754.  *        There is a chance that we will upgrade "encoding" to a "higher"
  4755.  *        level.  For example, if it comes in as 7BIT we may change
  4756.  *        that to 8BIT if we find a From_ we want to escape.
  4757.  *
  4758.  * NOTE: this is rather inefficient if the store object is a CharStar
  4759.  *       but the win is all types are handled the same
  4760.  */
  4761. void
  4762. set_mime_type_by_grope(body)
  4763.     BODY *body;
  4764. {
  4765. #define RBUFSZ    (8193)
  4766.     unsigned char   *buf, *p, *bol;
  4767.     register size_t  n;
  4768.     long             max_line = 0L,
  4769.                      eight_bit_chars = 0L,
  4770.                      line_so_far = 0L,
  4771.                      len = 0L,
  4772.                      can_be_ascii = 1L;
  4773.     STORE_S         *so = (STORE_S *)body->contents.binary;
  4774.     unsigned short   new_encoding = ENCOTHER;
  4775.     int              we_cancel = 0;
  4776. #ifdef ENCODE_FROMS
  4777.     short            froms = 0, dots = 0,
  4778.                      bmap  = 0x1, dmap = 0x1;
  4779. #endif
  4780.  
  4781.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  4782.  
  4783. #ifndef DOS
  4784.     buf = (unsigned char *)fs_get(RBUFSZ);
  4785. #else
  4786.     buf = (unsigned char *)tmp_20k_buf;
  4787. #endif
  4788.     so_seek(so, 0L, 0);
  4789.  
  4790.     for(n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
  4791.       ;
  4792.  
  4793.     buf[n] = '\0';
  4794.  
  4795.     if(n){    /* check first few bytes to look for magic numbers */
  4796.     if(body->type == TYPEOTHER){
  4797.         if(!strncmp((char *)buf, "GIF", 3)){
  4798.         body->type    = TYPEIMAGE;
  4799.         body->subtype = cpystr("GIF");
  4800.         }
  4801.         else if((n > 9) && buf[0] == 0xFF && buf[1] == 0xD8
  4802.                     && buf[2] == 0xFF && buf[3] == 0xE0
  4803.                     && !strncmp((char *)&buf[6], "JFIF", 4)){
  4804.             body->type    = TYPEIMAGE;
  4805.             body->subtype = cpystr("JPEG");
  4806.         }
  4807.         else if(!strncmp((char *)buf, "MM", 2)
  4808.                     || !strncmp((char *)buf, "II", 2)){
  4809.         body->type    = TYPEIMAGE;
  4810.         body->subtype = cpystr("TIFF");
  4811.         }
  4812.         else if((buf[0] == '%' && buf[1] == '!')
  4813.              || (buf[0] == '\004' && buf[1] == '%' && buf[2] == '!')){
  4814.         body->type    = TYPEAPPLICATION;
  4815.         body->subtype = cpystr("PostScript");
  4816.         }
  4817.         else if(!strncmp((char *)buf, ".snd", 4)){
  4818.         body->type    = TYPEAUDIO;
  4819.         body->subtype = cpystr("Basic");
  4820.         }
  4821.         else if((n > 3) && buf[0] == 0x00 && buf[1] == 0x05
  4822.                     && buf[2] == 0x16 && buf[3] == 0x00){
  4823.             body->type    = TYPEAPPLICATION;
  4824.         body->subtype = cpystr("APPLEFILE");
  4825.         }
  4826.         else if((n > 3) && buf[0] == 0x50 && buf[1] == 0x4b
  4827.                     && buf[2] == 0x03 && buf[3] == 0x04){
  4828.             body->type    = TYPEAPPLICATION;
  4829.         body->subtype = cpystr("ZIP");
  4830.         }
  4831.  
  4832.         /*
  4833.          * if type was set above, but no encoding specified, go
  4834.          * ahead and make it BASE64...
  4835.          */
  4836.         if(body->type != TYPEOTHER && body->encoding == ENCOTHER)
  4837.           body->encoding = ENCBINARY;
  4838.     }
  4839.     }
  4840.     else{
  4841.     /* PROBLEM !!! */
  4842.     if(body->type == TYPEOTHER){
  4843.         body->type = TYPEAPPLICATION;
  4844.         body->subtype = cpystr("octet-stream");
  4845.         if(body->encoding == ENCOTHER)
  4846.         body->encoding = ENCBINARY;
  4847.     }
  4848.     }
  4849.  
  4850.     if (body->encoding == ENCOTHER || body->type == TYPEOTHER){
  4851. #if defined(DOS) || defined(OS2) /* for binary file detection */
  4852.     int lastchar = '\0';
  4853. #endif
  4854.     p = bol = buf;
  4855.     len = n;
  4856.     while (n--){
  4857. /* Some people don't like quoted-printable caused by leading Froms */
  4858. #ifdef ENCODE_FROMS
  4859.         Find_Froms(froms, dots, bmap, dmap, *p);
  4860. #endif
  4861.         if(*p == '\n'){
  4862.         max_line = max(max_line, line_so_far + p - bol);
  4863.         bol = p;
  4864.         line_so_far = 0L;
  4865.         }
  4866.         else if(*p == ctrl('O') || *p == ctrl('N') || *p == ESCAPE){
  4867.         can_be_ascii--;
  4868.         }
  4869.         else if(*p & 0x80){
  4870.         eight_bit_chars++;
  4871.         }
  4872.         else if(!*p){
  4873.         /* NULL found. Unless we're told otherwise, must be binary */
  4874.         if(body->type == TYPEOTHER){
  4875.             body->type    = TYPEAPPLICATION;
  4876.             body->subtype = cpystr("octet-stream");
  4877.         }
  4878.  
  4879.         /*
  4880.          * The "TYPETEXT" here handles the case that the NULL
  4881.          * comes from imported text generated by some external
  4882.          * editor that permits or inserts NULLS.  Otherwise,
  4883.          * assume it's a binary segment...
  4884.          */
  4885.         new_encoding = (body->type==TYPETEXT) ? ENC8BIT : ENCBINARY;
  4886.  
  4887.         /*
  4888.          * Since we've already set encoding, count this as a 
  4889.          * hi bit char and continue.  The reason is that if this
  4890.          * is text, there may be a high percentage of encoded 
  4891.          * characters, so base64 may get set below...
  4892.          */
  4893.         if(body->type == TYPETEXT)
  4894.           eight_bit_chars++;
  4895.         else
  4896.           break;
  4897.         }
  4898. #if defined(DOS) || defined(OS2) /* for binary file detection */
  4899. #define BREAKOUT 300   /* a value that a character can't be */
  4900.         /* LF with no preceding CR, so binary */
  4901.         else if(*p == '\n' && lastchar != '\r'){
  4902.         lastchar = BREAKOUT;
  4903.         }
  4904. #endif
  4905.  
  4906. #if defined(DOS) || defined(OS2) /* for binary file detection */
  4907.         if(lastchar != BREAKOUT)
  4908.           lastchar = *p;
  4909. #endif
  4910.  
  4911.         /* read another buffer in */
  4912.         if(n == 0){
  4913.         line_so_far += p - bol;
  4914.         for (n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
  4915.           ;
  4916.         len += n;
  4917.         p = buf;
  4918.         }
  4919.         else{
  4920.         p++;
  4921.         }
  4922. #if defined(DOS) || defined(OS2) /* for binary file detection */
  4923.         /* either a lone \r or lone \n indicate binary file */
  4924.         if(lastchar == '\r' || lastchar == BREAKOUT){
  4925.         if(lastchar == BREAKOUT || n == 0 || *p != '\n'){
  4926.             if(body->type == TYPEOTHER){
  4927.             body->type    = TYPEAPPLICATION;
  4928.             body->subtype = cpystr("octet-stream");
  4929.             }
  4930.  
  4931.             new_encoding = ENCBINARY;
  4932.             break;
  4933.         }
  4934.         }
  4935. #endif
  4936.     }
  4937.     }
  4938.  
  4939.     if(body->encoding == ENCOTHER || body->type == TYPEOTHER){
  4940.     /*
  4941.      * Since the type or encoding aren't set yet, fall thru a 
  4942.      * series of tests to make sure an adequate type and 
  4943.      * encoding are set...
  4944.      */
  4945.  
  4946.     if(max_line >= 1000L){         /* 1000 comes from rfc821 */
  4947.         if(body->type == TYPEOTHER){
  4948.         /*
  4949.          * Since the types not set, then we didn't find a NULL.
  4950.          * If there's no NULL, then this is likely text.  However,
  4951.          * since we can't be *completely* sure, we set it to
  4952.          * the generic type.
  4953.          */
  4954.         body->type    = TYPEAPPLICATION;
  4955.         body->subtype = cpystr("octet-stream");
  4956.         }
  4957.  
  4958.         if(new_encoding != ENCBINARY)
  4959.           /*
  4960.            * As with NULL handling, if we're told it's text, 
  4961.            * qp-encode it, else it gets base 64...
  4962.            */
  4963.           new_encoding = (body->type == TYPETEXT) ? ENC8BIT : ENCBINARY;
  4964.     }
  4965.  
  4966.     if(eight_bit_chars == 0L){
  4967.         if(body->type == TYPEOTHER)
  4968.           body->type = TYPETEXT;
  4969.  
  4970.         if(new_encoding == ENCOTHER)
  4971.           new_encoding = ENC7BIT;  /* short lines, no 8 bit */
  4972.     }
  4973.     else if(len <= 3000L || (eight_bit_chars * 100L)/len < 30L){
  4974.         /*
  4975.          * The 30% threshold is based on qp encoded readability
  4976.          * on non-MIME UA's.
  4977.          */
  4978.         can_be_ascii--;
  4979.         if(body->type == TYPEOTHER)
  4980.           body->type = TYPETEXT;
  4981.  
  4982.         if(new_encoding != ENCBINARY)
  4983.           new_encoding = ENC8BIT;  /* short lines, < 30% 8 bit chars */
  4984.     }
  4985.     else{
  4986.         can_be_ascii--;
  4987.         if(body->type == TYPEOTHER){
  4988.         body->type    = TYPEAPPLICATION;
  4989.         body->subtype = cpystr("octet-stream");
  4990.         }
  4991.  
  4992.         /*
  4993.          * Apply maximal encoding regardless of previous
  4994.          * setting.  This segment's either not text, or is 
  4995.          * unlikely to be readable with > 30% of the
  4996.          * text encoded anyway, so we might as well save space...
  4997.          */
  4998.         new_encoding = ENCBINARY;   /*  > 30% 8 bit chars */
  4999.     }
  5000.     }
  5001.  
  5002. #ifdef ENCODE_FROMS
  5003.     /* If there were From_'s at the beginning of a line or standalone dots */
  5004.     if((froms || dots) && new_encoding != ENCBINARY)
  5005.       new_encoding = ENC8BIT;
  5006. #endif
  5007.  
  5008.     /* need to set the subtype, and possibly the charset */
  5009.     if(body->type == TYPETEXT && body->subtype == NULL){
  5010.         PARAMETER *pm;
  5011.  
  5012.     body->subtype = cpystr("PLAIN");
  5013.  
  5014.     /* need to add charset */
  5015.     if(can_be_ascii > 0 || ps_global->VAR_CHAR_SET){
  5016.         if(body->parameter == NULL){
  5017.             pm = body->parameter = mail_newbody_parameter();
  5018.             pm->attribute = cpystr("charset");
  5019.         }
  5020.         else{
  5021.                 for(pm = body->parameter;
  5022.             strucmp(pm->attribute, "charset") && pm->next != NULL;
  5023.             pm = pm->next)
  5024.           ;/* find charset parameter */
  5025.  
  5026.             if(strucmp(pm->attribute, "charset") != 0){ /* add one */
  5027.             pm->next = mail_newbody_parameter();
  5028.             pm = pm->next;
  5029.             pm->attribute = cpystr("charset");
  5030.             }
  5031.         else if(pm->value)
  5032.           fs_give((void **)&pm->value);
  5033.         }
  5034.  
  5035.         if(can_be_ascii > 0)
  5036.           pm->value = cpystr("US-ASCII");
  5037.         else
  5038.           pm->value = cpystr(ps_global->VAR_CHAR_SET);
  5039.     }
  5040.     }
  5041.  
  5042.     /* need to set the charset */
  5043.     if(body->type == TYPEAPPLICATION
  5044.        && body->subtype
  5045.        && strucmp(body->subtype, "DIRECTORY") == 0
  5046.        && body->parameter
  5047.        && strucmp(body->parameter->attribute, "PROFILE") == 0
  5048.        && strucmp(body->parameter->value, "X-Email-Abook-Entry") == 0
  5049.        && (can_be_ascii > 0 || ps_global->VAR_CHAR_SET)){
  5050.         PARAMETER *pm;
  5051.  
  5052.     for(pm = body->parameter;
  5053.         strucmp(pm->attribute, "charset") && pm->next != NULL;
  5054.         pm = pm->next)
  5055.       ;/* find charset parameter */
  5056.  
  5057.     if(strucmp(pm->attribute, "charset") != 0){  /* wasn't one, add it */
  5058.         pm->next = mail_newbody_parameter();
  5059.         pm = pm->next;
  5060.         pm->attribute = cpystr("charset");
  5061.         if(can_be_ascii > 0)
  5062.           pm->value = cpystr("US-ASCII");
  5063.         else
  5064.           pm->value = cpystr(ps_global->VAR_CHAR_SET);
  5065.     }
  5066.     /* else, leave what was there */
  5067.     }
  5068.  
  5069.     if(body->encoding == ENCOTHER)
  5070.       body->encoding = new_encoding;
  5071.  
  5072. #ifndef    DOS
  5073.     fs_give((void **)&buf);
  5074. #endif
  5075.  
  5076.     if(we_cancel)
  5077.       cancel_busy_alarm(-1);
  5078. }
  5079.  
  5080.  
  5081. /*
  5082.  *
  5083.  */
  5084. void
  5085. set_body_size(b)
  5086.     BODY *b;
  5087. {
  5088.     unsigned char c;
  5089.     int we_cancel = 0;
  5090.  
  5091.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5092.     so_seek((STORE_S *)b->contents.binary, 0L, 0);
  5093.     b->size.bytes = 0L;
  5094.     while(so_readc(&c, (STORE_S *)b->contents.binary))
  5095.       b->size.bytes++;
  5096.  
  5097.     if(we_cancel)
  5098.       cancel_busy_alarm(-1);
  5099. }
  5100.  
  5101.  
  5102.  
  5103. /*
  5104.  * pine_header_line - simple wrapper around c-client call to contain
  5105.  *                    repeated code, and to write fcc if required.
  5106.  */
  5107. int
  5108. pine_header_line(tmp, field, header, text, f, s, writehdr, localcopy)
  5109.     char      *tmp;
  5110.     char      *field;
  5111.     METAENV   *header;
  5112.     char      *text;
  5113.     soutr_t    f;
  5114.     TCPSTREAM *s;
  5115.     int        writehdr, localcopy;
  5116. {
  5117.     char *p;
  5118.  
  5119.     *(p = tmp) = '\0';
  5120.     rfc822_header_line(&p, field, header ? header->env : NULL,
  5121.                rfc1522_encode(tmp_20k_buf, (unsigned char *) text,
  5122.                       ps_global->VAR_CHAR_SET));
  5123.     return(((writehdr && f) ? (*f)(s, tmp) : 1)
  5124.          && ((localcopy && local_so
  5125.          && !local_written) ? so_puts(local_so,tmp) : 1));
  5126. }
  5127.  
  5128.  
  5129. /*
  5130.  * pine_address_line - write a header field containing addresses,
  5131.  *                     one by one (so there's no buffer limit), and
  5132.  *                     wrapping where necessary.
  5133.  * Note: we use c-client functions to properly build the text string,
  5134.  *       but have to screw around with pointers to fool c-client functions
  5135.  *       into not blatting all the text into a single buffer.  Yeah, I know.
  5136.  */
  5137. int
  5138. pine_address_line(tmp, field, header, alist, f, s, writehdr, localcopy)
  5139.     char      *tmp;
  5140.     char      *field;
  5141.     METAENV   *header;
  5142.     ADDRESS   *alist;
  5143.     soutr_t    f;
  5144.     TCPSTREAM *s;
  5145.     int        writehdr, localcopy;
  5146. {
  5147.     char    *p, *delim, *ptmp, *pruned, ch;
  5148.     ADDRESS *atmp;
  5149.     int      i, count, write_field;
  5150.     int      in_group = 0, was_start_of_group = 0;
  5151.     static   char comma[]    = ", ";
  5152. #define    no_comma    (&comma[1])
  5153.  
  5154.     if(!alist)                /* nothing in field! */
  5155.       return(1);
  5156.  
  5157.     *(p = tmp)  = '\0';
  5158.     if(!alist->host && alist->mailbox){ /* c-client group convention */
  5159.         in_group = 1;
  5160.     was_start_of_group = 1;
  5161.     }
  5162.  
  5163.     write_field        = !sending_stream || writehdr;
  5164.     ptmp        = alist->personal;    /* remember personal name */
  5165.     alist->personal = rfc1522_encode(tmp_20k_buf,
  5166.                      (unsigned char *) alist->personal,
  5167.                      ps_global->VAR_CHAR_SET);
  5168.     pruned        = prune_personal(alist, &ch);
  5169.     atmp            = alist->next;
  5170.     alist->next        = NULL;        /* digest only first address! */
  5171.     rfc822_address_line(&p, field, header ? header->env : NULL, alist);
  5172.     alist->next        = atmp;        /* restore pointer to next addr */
  5173.     alist->personal = ptmp;        /* in case it changed, restore name */
  5174.     if(pruned)                /* restore truncated string */
  5175.       *pruned = ch;
  5176.  
  5177.     if((count = strlen(tmp)) > 2){    /* back over CRLF */
  5178.     count -= 2;
  5179.     tmp[count] = '\0';
  5180.     }
  5181.  
  5182.     if((write_field && f && !(*f)(s, tmp))
  5183.        || (localcopy && local_so && !local_written && !so_puts(local_so, tmp)))
  5184.       return(0);
  5185.  
  5186.     for(alist = atmp; alist; alist = alist->next){
  5187.     delim = comma;
  5188.     /* account for c-client's representation of group names */
  5189.     if(in_group){
  5190.         if(!alist->host){  /* end of group */
  5191.         in_group = 0;
  5192.         was_start_of_group = 0;
  5193.         delim = no_comma;
  5194.         }
  5195.         else if(!localcopy || !local_so || local_written)
  5196.           continue;
  5197.     }
  5198.     /* start of new group, print phrase below */
  5199.         else if(!alist->host && alist->mailbox){
  5200.         in_group = 1;
  5201.         was_start_of_group = 1;
  5202.     }
  5203.  
  5204.     /* no comma before first address in group syntax */
  5205.     if(was_start_of_group && alist->host){
  5206.         delim = no_comma;
  5207.         was_start_of_group = 0;
  5208.     }
  5209.  
  5210.     /* first, write delimiter */
  5211.     if(((!in_group && write_field && f && !(*f)(s, delim))
  5212.        || (localcopy && local_so && !local_written
  5213.            && !so_puts(local_so, delim))))
  5214.       return(0);
  5215.  
  5216.     tmp[0]        = '\0';
  5217.     ptmp        = alist->personal; /* remember personal name */
  5218.     alist->personal = rfc1522_encode(tmp_20k_buf,
  5219.                      (unsigned char *) alist->personal,
  5220.                      ps_global->VAR_CHAR_SET);
  5221.     pruned        = prune_personal(alist, &ch);
  5222.     atmp        = alist->next;
  5223.     alist->next    = NULL;        /* tie off linked list */
  5224.     rfc822_write_address(tmp, alist);
  5225.     alist->next    = atmp;        /* restore next pointer */
  5226.     alist->personal = ptmp;        /* in case it changed, restore name */
  5227.  
  5228.     /*
  5229.      * BUG
  5230.      * With group syntax addresses we no longer have two identical
  5231.      * streams of output.  Instead, for the fcc/postpone copy we include
  5232.      * all of the addresses inside the :; of the group, and for the
  5233.      * mail we're sending we don't include them.  That means we aren't
  5234.      * correctly keeping track of the column to wrap in, below.  That is,
  5235.      * we are keeping track of the fcc copy but we aren't keeping track
  5236.      * of the regular copy.  It could result in too long or too short
  5237.      * lines.  Should almost never come up since group addresses are almost
  5238.      * never followed by other addresses in the same header, and even
  5239.      * when they are, you have to go out of your way to get the headers
  5240.      * messed up.
  5241.      */
  5242.     if(count + 2 + (i = strlen(tmp)) > 78){ /* wrap long lines... */
  5243.         count = i + 4;
  5244.         if((!in_group && write_field && f && !(*f)(s, "\015\012    "))
  5245.            || (localcopy && local_so && !local_written &&
  5246.                        !so_puts(local_so, "\015\012    ")))
  5247.           return(0);
  5248.     }
  5249.     else
  5250.       count += i + 2;
  5251.  
  5252.     if((!in_group && write_field && f && !(*f)(s, tmp))
  5253.        || (localcopy && local_so && !local_written
  5254.            && !so_puts(local_so, tmp)))
  5255.       return(0);
  5256.     }
  5257.  
  5258.     return((write_field && f ? (*f)(s, "\015\012") : 1)
  5259.        && ((localcopy && local_so
  5260.            && !local_written) ? so_puts(local_so, "\015\012") : 1));
  5261. }
  5262.  
  5263.  
  5264. /*
  5265.  * prune_personal - function to help make sure we don't pop any c-client
  5266.  *            fixed length buffers.  Yeah, I know.
  5267.  *
  5268.  * Returns: NULL if things look ok, else pointer into some ADDRESS
  5269.  *        structure string that got truncated, and the character the
  5270.  *        new terminating NULL replaced so the string can be restored.
  5271.  */
  5272. char *
  5273. prune_personal(addr, c)
  5274.     ADDRESS *addr;
  5275.     char    *c;
  5276. {
  5277.     char *p = NULL;
  5278.  
  5279.     if(addr && addr->personal && strlen(addr->personal) > MAX_SINGLE_ADDR/2){
  5280.     p = &addr->personal[MAX_SINGLE_ADDR/2];
  5281.     *c = *p;
  5282.     *p = '\0';
  5283.     }
  5284.  
  5285.     return(p);
  5286. }
  5287.  
  5288.  
  5289. /*
  5290.  * mutated pine version of c-client's rfc822_header() function. 
  5291.  * changed to call pine-wrapped header and address functions
  5292.  * so we don't have to limit the header size to a fixed buffer.
  5293.  * This function also calls pine's body_header write function
  5294.  * because encoding is delayed until output_body() is called.
  5295.  */
  5296. int
  5297. pine_rfc822_header(header, body, f, s)
  5298.     METAENV   *header;
  5299.     BODY      *body;
  5300.     soutr_t    f;
  5301.     TCPSTREAM *s;
  5302. {
  5303.     char        tmp[MAX_SINGLE_ADDR], *p;
  5304.     PINEFIELD  *pf;
  5305.     int         j;
  5306.  
  5307.     if(header->env->remail){            /* if remailing */
  5308.     long i = strlen (header->env->remail);
  5309.     if(i > 4 && header->env->remail[i-4] == '\015')
  5310.       header->env->remail[i-2] = '\0'; /* flush extra blank line */
  5311.  
  5312.     if((f && !(*f)(s, header->env->remail)) 
  5313.       || (local_so && !local_written
  5314.           && !so_puts(local_so, header->env->remail)))
  5315.       return(0);                /* start with remail header */
  5316.     }
  5317.  
  5318.     j = 0;
  5319.     for(pf = header->sending_order[j]; pf; pf = header->sending_order[++j]){
  5320.     switch(pf->type){
  5321.       case Address:
  5322.         if(!pine_address_line(tmp, pf->name, header,
  5323.         pf->addr ? *pf->addr : NULL,
  5324.         f, s, pf->writehdr, pf->localcopy))
  5325.           return(0);
  5326.  
  5327.         break;
  5328.  
  5329.       case Fcc:
  5330.       case FreeText:
  5331.       case Subject:
  5332.         if(!pine_header_line(tmp, pf->name, header,
  5333.         pf->text ? *pf->text : NULL,
  5334.         f, s, pf->writehdr, pf->localcopy))
  5335.           return(0);
  5336.  
  5337.         break;
  5338.  
  5339.       default:
  5340.         q_status_message1(SM_ORDER,3,7,"Unknown header type: %s",pf->name);
  5341.         break;
  5342.     }
  5343.     }
  5344.  
  5345.  
  5346. #if    (defined(DOS) || defined(OS2)) && !defined(NOAUTH)
  5347.     /*
  5348.      * Add comforting "X-" header line indicating what sort of 
  5349.      * authenticity the receiver can expect...
  5350.      */
  5351.     {
  5352.      NETMBX netmbox;
  5353.      char   sstring[MAILTMPLEN], *label;    /* place to write  */
  5354.  
  5355.      if((ps_global->mail_stream
  5356.          && mail_valid_net_parse(ps_global->mail_stream->mailbox, &netmbox)
  5357.          && !netmbox.anoflag)
  5358.         || (ps_global->inbox_stream
  5359.         && ps_global->inbox_stream != ps_global->mail_stream
  5360.         && mail_valid_net_parse(ps_global->inbox_stream->mailbox,
  5361.                     &netmbox)
  5362.         && !netmbox.anoflag)){
  5363.          char  last_char = netmbox.host[strlen(netmbox.host) - 1],
  5364.           *user = !strncmp(ps_global->mail_stream->dtb->name,"imap",4)
  5365.                 ? imap_user(ps_global->mail_stream)
  5366.                 : cached_user_name(netmbox.mailbox);
  5367.          sprintf(sstring, "%s@%s%s%s", user ? user : "NULL", 
  5368.              isdigit(last_char) ? "[" : "",
  5369.              netmbox.host, isdigit(last_char) ? "]" : "");
  5370.          label = F_ON(F_USE_SENDER_NOT_X, ps_global)?"Sender":"X-Sender";
  5371.      }
  5372.      else{
  5373.          strcpy(sstring,"UNAuthenticated Sender");
  5374.          label = "X-Warning";
  5375.      }
  5376.  
  5377.      if(!pine_header_line(tmp, label, header, sstring, f, s, 1, 1))
  5378.        return(0);
  5379.      }
  5380. #endif
  5381.  
  5382.     if(body && !header->env->remail){    /* not if remail or no body */
  5383.     if((f && !(*f)(s, MIME_VER))
  5384.        || (local_so && !local_written && !so_puts(local_so, MIME_VER)))
  5385.       return(0);
  5386.  
  5387.     *(p = tmp) = '\0';
  5388.     pine_write_body_header(&p, body);
  5389.  
  5390.     if((f && !(*f)(s, tmp)) 
  5391.        || (local_so && !local_written && !so_puts(local_so, tmp)))
  5392.       return(0);
  5393.     }
  5394.     else{                    /* write terminating newline */
  5395.     if((f && !(*f)(s, "\015\012"))
  5396.        || (local_so && !local_written && !so_puts(local_so, "\015\012")))
  5397.       return(0);
  5398.     }
  5399.  
  5400.     return(1);
  5401. }
  5402.  
  5403.  
  5404. /*
  5405.  * since encoding happens on the way out the door, this is basically
  5406.  * just needed to handle TYPEMULTIPART
  5407.  */
  5408. void
  5409. pine_encode_body (body)
  5410.     BODY *body;
  5411. {
  5412.   void *f;
  5413.   PART *part;
  5414.   dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
  5415.   if (body) switch (body->type) {
  5416.   case TYPEMULTIPART:        /* multi-part */
  5417.     if (!body->parameter) {    /* cookie not set up yet? */
  5418.       char tmp[MAILTMPLEN];    /* make cookie not in BASE64 or QUOTEPRINT*/
  5419.       sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
  5420.            getpid ());
  5421.       body->parameter = mail_newbody_parameter ();
  5422.       body->parameter->attribute = cpystr ("BOUNDARY");
  5423.       body->parameter->value = cpystr (tmp);
  5424.     }
  5425.     part = body->contents.part;    /* encode body parts */
  5426.     do pine_encode_body (&part->body);
  5427.     while (part = part->next);    /* until done */
  5428.     break;
  5429. /* case MESSAGE:    */    /* here for documentation */
  5430.     /* Encapsulated messages are always treated as text objects at this point.
  5431.        This means that you must replace body->contents.msg with
  5432.        body->contents.text, which probably involves copying
  5433.        body->contents.msg.text to body->contents.text */
  5434.   default:            /* all else has some encoding */
  5435.     /*
  5436.      * but we'll delay encoding it until the message is on the way
  5437.      * into the mail slot...
  5438.      */
  5439.     break;
  5440.   }
  5441. }
  5442.  
  5443.  
  5444. /*
  5445.  * post_rfc822_output - cloak for pine's 822 output routine.  Since
  5446.  *            we can't pass opaque envelope thru c-client posting
  5447.  *            logic, we need to wrap the real output inside
  5448.  *            something that c-client knows how to call.
  5449.  */
  5450. int
  5451. post_rfc822_output (tmp, env, body, f, s)
  5452.     char      *tmp;
  5453.     ENVELOPE  *env;
  5454.     BODY      *body;
  5455.     soutr_t    f;
  5456.     TCPSTREAM *s;
  5457. {
  5458.     return(pine_rfc822_output(send_header, body, f, s));
  5459. }
  5460.  
  5461.  
  5462. /*
  5463.  * pine_rfc822_output - pine's version of c-client call.  Necessary here
  5464.  *            since we're not using its structures as intended!
  5465.  */
  5466. int
  5467. pine_rfc822_output(header, body, f, s)
  5468.     METAENV   *header;
  5469.     BODY      *body;
  5470.     soutr_t    f;
  5471.     TCPSTREAM *s;
  5472. {
  5473.     int we_cancel = 0;
  5474.     int retval;
  5475.  
  5476.     dprint(4, (debugfile, "-- pine_rfc822_output\n"));
  5477.  
  5478.     we_cancel = busy_alarm(1, NULL, NULL, 0);
  5479.     pine_encode_body(body);        /* encode body as necessary */
  5480.     /* build and output RFC822 header, output body */
  5481.     retval = pine_rfc822_header(header, body, f, s)
  5482.        && (body ? pine_rfc822_output_body(body, f, s) : 1);
  5483.  
  5484.     if(we_cancel)
  5485.       cancel_busy_alarm(-1);
  5486.  
  5487.     return(retval);
  5488. }
  5489.  
  5490.  
  5491. /*
  5492.  * Local globals pine's body output routine needs
  5493.  */
  5494. static soutr_t    l_f;
  5495. static TCPSTREAM *l_stream;
  5496. static unsigned   c_in_buf = 0;
  5497.  
  5498.  
  5499. /*
  5500.  * l_flust_net - empties gf_io terminal function's buffer
  5501.  */
  5502. void
  5503. l_flush_net()
  5504. {
  5505.     if(c_in_buf){
  5506.     if(l_f)
  5507.       (*l_f)(l_stream, tmp_20k_buf);
  5508.  
  5509.     if(local_so && !local_written)
  5510.       so_puts(local_so, tmp_20k_buf);
  5511.     }
  5512.  
  5513.     c_in_buf = 0;
  5514. }
  5515.  
  5516.  
  5517. /*
  5518.  * l_putc - gf_io terminal function that calls smtp's soutr_t function.
  5519.  *
  5520.  */
  5521. int
  5522. l_putc(c)
  5523.     int c;
  5524. {
  5525.     tmp_20k_buf[c_in_buf++] = (char) c;
  5526.     tmp_20k_buf[c_in_buf]   = '\0';
  5527.  
  5528.     if(c_in_buf > 16384){
  5529.     if(local_so && !local_written)
  5530.       so_puts(local_so, tmp_20k_buf);
  5531.  
  5532.         c_in_buf = 0;
  5533.         return(l_f ? (int)(*l_f)(l_stream, tmp_20k_buf) : 1);
  5534.     }
  5535.     else
  5536.     return(TRUE);
  5537. }
  5538.  
  5539.  
  5540.  
  5541. /*
  5542.  * pine_rfc822_output_body - pine's version of c-client call.  Again, 
  5543.  *                necessary since c-client doesn't know about how
  5544.  *                we're treating attachments
  5545.  */
  5546. long
  5547. pine_rfc822_output_body(body, f, s)
  5548.     BODY *body;
  5549.     soutr_t f;
  5550.     TCPSTREAM *s;
  5551. {
  5552.     PART *part;
  5553.     PARAMETER *param;
  5554.     char *cookie = NIL, *t, *encode_error;
  5555.     char tmp[MAILTMPLEN];
  5556.     gf_io_t            gc;
  5557. #if defined(DOS) || defined(OS2)
  5558.     extern unsigned char  *xlate_from_codepage;
  5559. #endif
  5560.  
  5561.     dprint(4, (debugfile, "-- pine_rfc822_output_body: %d\n",
  5562.            body ? body->type : 0));
  5563.     if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
  5564.     part = body->contents.part;    /* first body part */
  5565.                     /* find cookie */
  5566.     for (param = body->parameter; param && !cookie; param = param->next)
  5567.       if (!strcmp (param->attribute,"BOUNDARY")) cookie = param->value;
  5568.     if (!cookie) cookie = "-";    /* yucky default */
  5569.  
  5570.     /*
  5571.      * Output a bit of text before the first multipart delimiter
  5572.      * to warn unsuspecting users of non-mime-aware ua's that
  5573.      * they should expect weirdness...
  5574.      */
  5575.     if(f && !(*f)(s, "  This message is in MIME format.  The first part should be readable text,\015\012  while the remaining parts are likely unreadable without MIME-aware tools.\015\012  Send mail to mime@docserver.cac.washington.edu for more info.\015\012\015\012"))
  5576.       return(0);
  5577.  
  5578.     do {                /* for each part */
  5579.                     /* build cookie */
  5580.         sprintf (t = tmp,"--%s\015\012",cookie);
  5581.                     /* append mini-header */
  5582.         pine_write_body_header (&t,&part->body);
  5583.                 /* output cookie, mini-header, and contents */
  5584.         if(local_so && !local_written)
  5585.           so_puts(local_so, tmp);
  5586.  
  5587.         if (!((f ? (*f) (s,tmp) : 1)
  5588.           && pine_rfc822_output_body (&part->body,f,s)))
  5589.           return(0);
  5590.     } while (part = part->next);    /* until done */
  5591.                     /* output trailing cookie */
  5592.     sprintf (t = tmp,"--%s--",cookie);
  5593.     if(local_so && !local_written){
  5594.         so_puts(local_so, t);
  5595.         so_puts(local_so, "\015\012");
  5596.     }
  5597.  
  5598.     return(f ? ((*f) (s,t) && (*f) (s,"\015\012")) : 1);
  5599.     }
  5600.  
  5601.     l_f      = f;            /* set up for writing chars...  */
  5602.     l_stream = s;            /* out other end of pipe...     */
  5603.     gf_filter_init();
  5604.     dprint(4, (debugfile, "-- pine_rfc822_output_body: segment %ld bytes\n",
  5605.            body->size.bytes));
  5606.  
  5607.     if(body->contents.binary)
  5608.       gf_set_so_readc(&gc, (STORE_S *)body->contents.binary);
  5609.     else
  5610.       return(1);
  5611.  
  5612.     so_seek((STORE_S *)body->contents.binary, 0L, 0);
  5613.  
  5614.     if(body->type != TYPEMESSAGE){     /* NOT encapsulated message */
  5615.     /*
  5616.      * Convert text pieces to canonical form
  5617.      * BEFORE applying any encoding (rfc1341: appendix G)...
  5618.      */
  5619.     if(body->type == TYPETEXT){
  5620.         gf_link_filter(gf_local_nvtnl);
  5621.         gf_link_filter(gf_delete_trail_space);
  5622.  
  5623. #if defined(DOS) || defined(OS2)
  5624.         if(xlate_from_codepage){
  5625.         gf_translate_opt(xlate_from_codepage, 256);
  5626.         gf_link_filter(gf_translate);
  5627.         }
  5628. #endif
  5629.     }
  5630.  
  5631.     switch (body->encoding) {    /* all else needs filtering */
  5632.       case ENC8BIT:            /* encode 8BIT into QUOTED-PRINTABLE */
  5633.         if(!(sending_stream && sending_stream->ok_8bitmime))
  5634.           gf_link_filter(gf_8bit_qp);
  5635.  
  5636.         break;
  5637.  
  5638.       case ENCBINARY:        /* encode binary into BASE64 */
  5639.         gf_link_filter(gf_binary_b64);
  5640.         break;
  5641.  
  5642.       default:            /* otherwise text */
  5643.         break;
  5644.     }
  5645.     }
  5646.  
  5647.     if(encode_error = gf_pipe(gc, l_putc)){ /* shove body part down pipe */
  5648.     q_status_message1(SM_ORDER | SM_DING, 3, 4,
  5649.               "Encoding Error \"%s\"", encode_error);
  5650.     display_message('x');
  5651.     return(0);
  5652.     }
  5653.     else
  5654.       l_flush_net();
  5655.  
  5656.     send_bytes_sent += gf_bytes_piped();
  5657.     so_release((STORE_S *)body->contents.binary);
  5658.  
  5659.     if(local_so && !local_written)
  5660.       so_puts(local_so, "\015\012");
  5661.  
  5662.     return(f ? (*f)(s, "\015\012") : 1);    /* output final stuff */
  5663. }
  5664.  
  5665.  
  5666. /*
  5667.  * pine_write_body_header - another c-client clone.  This time only
  5668.  *                          so the final encoding labels get set 
  5669.  *                          correctly since it hasn't happened yet.
  5670.  */
  5671. void
  5672. pine_write_body_header(dst, body)
  5673.     char **dst;
  5674.     BODY  *body;
  5675. {
  5676.     char *s;
  5677.     PARAMETER *param = body->parameter;
  5678.     extern const char *tspecials;
  5679.  
  5680.     sprintf (*dst += strlen (*dst),"Content-Type: %s",body_types[body->type]);
  5681.     s = body->subtype ? body->subtype : rfc822_default_subtype (body->type);
  5682.     sprintf (*dst += strlen (*dst),"/%s",s);
  5683.     if (param) do {
  5684.     sprintf (*dst += strlen (*dst),"; %s=",param->attribute);
  5685.     rfc822_cat (*dst,param->value,tspecials);
  5686.     } while (param = param->next);
  5687.     else if (body->type == TYPETEXT) strcat (*dst,"; charset=US-ASCII");
  5688.     strcpy (*dst += strlen (*dst),"\015\012");
  5689.     if (body->encoding)        /* note: encoding 7BIT never output! */
  5690.       sprintf (*dst += strlen (*dst),"Content-Transfer-Encoding: %s\015\012",
  5691.            body_encodings[body->encoding == ENCBINARY ? ENCBASE64 :
  5692.                   body->encoding == ENC8BIT
  5693.                     ? (!(sending_stream
  5694.                      && sending_stream->ok_8bitmime))
  5695.                      ? ENCQUOTEDPRINTABLE : ENC8BIT :
  5696.                   body->encoding <= ENCMAX ? body->encoding :
  5697.                              ENCOTHER]);
  5698.     if (body->id) sprintf (*dst += strlen (*dst),"Content-ID: %s\015\012",
  5699.                body->id);
  5700.     if (body->description)
  5701.       sprintf (*dst += strlen (*dst),"Content-Description: %s\015\012",
  5702.            body->description);
  5703.     strcat (*dst,"\015\012");    /* write terminating blank line */
  5704. }
  5705.  
  5706.  
  5707. /*
  5708.  * pine_free_body - yet another c-client clone just so the body gets
  5709.  *                  free'd appropriately
  5710.  */
  5711. void
  5712. pine_free_body(body)
  5713.     BODY **body;
  5714. {
  5715.     if (*body) {            /* only free if exists */
  5716.     pine_free_body_data (*body);    /* free its data */
  5717.     fs_give ((void **) body);    /* return body to free storage */
  5718.     }
  5719. }
  5720.  
  5721.  
  5722. /* 
  5723.  * pine_free_body_data - not just releasing strings anymore!
  5724.  */
  5725. void
  5726. pine_free_body_data(body)
  5727.     BODY *body;
  5728. {
  5729.     if (body->subtype) fs_give ((void **) &body->subtype);
  5730.     mail_free_body_parameter (&body->parameter);
  5731.     if (body->id) fs_give ((void **) &body->id);
  5732.     if (body->description) fs_give ((void **) &body->description);
  5733.     if(body->type == TYPEMULTIPART){
  5734.     pine_free_body_part (&body->contents.part);
  5735.     }
  5736.     else if(body->contents.binary){
  5737.     so_give((STORE_S **)&body->contents.binary);
  5738.     body->contents.binary = NULL;
  5739.     }
  5740. }
  5741.  
  5742.  
  5743. /*
  5744.  * pine_free_body_part - c-client clone to call the right routines
  5745.  *             for cleaning up.
  5746.  */
  5747. void
  5748. pine_free_body_part(part)
  5749.     PART **part;
  5750. {
  5751.     if (*part) {        /* only free if exists */
  5752.     pine_free_body_data (&(*part)->body);
  5753.                 /* run down the list as necessary */
  5754.     pine_free_body_part (&(*part)->next);
  5755.     fs_give ((void **) part); /* return body part to free storage */
  5756.     }
  5757. }
  5758.  
  5759.  
  5760. /*
  5761.  *
  5762.  */
  5763. int
  5764. sent_percent()
  5765. {
  5766.     int i = (int) (((send_bytes_sent + gf_bytes_piped()) * 100)
  5767.                             / send_bytes_to_send);
  5768.     return(min(i, 100));
  5769. }
  5770.  
  5771.  
  5772.  
  5773. /*
  5774.  *
  5775.  */
  5776. long
  5777. send_body_size(body)
  5778.     BODY *body;
  5779. {
  5780.     long  l = 0L;
  5781.     PART *part;
  5782.  
  5783.     if(body->type == TYPEMULTIPART) {   /* multipart gets special handling */
  5784.     part = body->contents.part;    /* first body part */
  5785.     do                /* for each part */
  5786.       l += send_body_size(&part->body);
  5787.     while (part = part->next);    /* until done */
  5788.     return(l);
  5789.     }
  5790.  
  5791.     return(l + body->size.bytes);
  5792. }
  5793.  
  5794.  
  5795.  
  5796. /*
  5797.  * pine_smtp_verbose - pine's contribution to the smtp stream.  Return
  5798.  *               TRUE for any positive reply code to our "VERBose"
  5799.  *               request.
  5800.  *
  5801.  *    NOTE: at worst, this command may cause the SMTP connection to get
  5802.  *          nuked.  Modern sendmail's recognize it, and possibly other
  5803.  *          SMTP implementations (the "ON" arg is for PMDF).  What's
  5804.  *          more, if it works, what's returned (sendmail uses reply code
  5805.  *          050, but we'll accept anything less than 100) may not get 
  5806.  *          recognized, and if it does the accompanying text will likely
  5807.  *          vary from server to server.
  5808.  */
  5809. long
  5810. pine_smtp_verbose(stream)
  5811.     SMTPSTREAM *stream;
  5812. {
  5813.     /* any 2xx reply to this is acceptable */
  5814.     return(smtp_send(stream,"VERB","ON")/100L == 2L);
  5815. }
  5816.  
  5817.  
  5818.  
  5819. /*
  5820.  * pine_smtp_verbose_out - write 
  5821.  */
  5822. void
  5823. pine_smtp_verbose_out(s)
  5824.     char *s;
  5825. {
  5826.     if(verbose_send_output && s){
  5827.     char *p, last = '\0';
  5828.  
  5829.     for(p = s; *p; p++)
  5830.       if(*p == '\015')
  5831.         *p = ' ';
  5832.       else
  5833.         last = *p;
  5834.  
  5835.     fputs(s, verbose_send_output);
  5836.     if(last != '\012')
  5837.       fputc('\n', verbose_send_output);
  5838.     }
  5839.  
  5840. }
  5841.  
  5842.  
  5843.  
  5844. /*----------------------------------------------------------------------
  5845.       Post news via NNTP or inews
  5846.  
  5847. Args: env -- envelope of message to post
  5848.        body -- body of message to post
  5849.  
  5850. Returns: -1 if failed or cancelled, 1 if succeeded
  5851.  
  5852. WARNING: This call function has the side effect of writing the message
  5853.     to the local_so object.   
  5854.   ----*/
  5855. news_poster(header, body)
  5856.      METAENV *header;
  5857.      BODY    *body;
  5858. {
  5859.     char *error_mess, error_buf[100];
  5860.     int      rv, we_cancel = 0;
  5861.     void *orig_822_output;
  5862.  
  5863.     we_cancel = busy_alarm(1, "Posting news", NULL, 1);
  5864.  
  5865.     dprint(4, (debugfile, "Posting: [%s]\n", header->env->newsgroups));
  5866.  
  5867.     if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
  5868.        && ps_global->VAR_NNTP_SERVER[0][0]){
  5869.        /*---------- NNTP server defined ----------*/
  5870.         error_mess              = NULL;
  5871.  
  5872.     /*
  5873.      * Install our rfc822 output routine 
  5874.      */
  5875.     orig_822_output = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
  5876.     (void) mail_parameters(NULL, SET_RFC822OUTPUT, post_rfc822_output);
  5877.  
  5878.         ps_global->noshow_error = 1;
  5879. #ifdef DEBUG
  5880.         sending_stream = nntp_open(ps_global->VAR_NNTP_SERVER, debug);
  5881. #else
  5882.         sending_stream = nntp_open(ps_global->VAR_NNTP_SERVER, 0L);
  5883. #endif
  5884.         ps_global->noshow_error = 0;
  5885.  
  5886.         if(sending_stream != NULL) {
  5887.         /*
  5888.          * Fake that we've got clearance from the transport agent
  5889.          * for 8bit transport for the benefit of our output routines...
  5890.          */
  5891.         if(F_ON(F_ENABLE_8BIT_NNTP, ps_global))
  5892.           sending_stream->ok_8bitmime = 1;
  5893.  
  5894.         /*
  5895.          * Set global header pointer so we can get at it later...
  5896.          */
  5897.         send_header = header;
  5898.  
  5899.             if(nntp_mail(sending_stream, header->env, body) == 0)
  5900.           sprintf(error_mess = error_buf, "Error posting message: %.60s",
  5901.               sending_stream->reply);
  5902.  
  5903.             smtp_close(sending_stream);
  5904.         sending_stream = NULL;
  5905.  
  5906.         } else {
  5907.             /*---- Open of NNTP connection failed ------ */
  5908.             sprintf(error_mess = error_buf,
  5909.             "Error connecting to news server: %.60s",
  5910.                     ps_global->c_client_error);
  5911.             dprint(1, (debugfile, error_buf));
  5912.         }
  5913.  
  5914.     (void) mail_parameters (NULL, SET_RFC822OUTPUT, orig_822_output);
  5915.     } else {
  5916.         /*----- Post via local mechanism -------*/
  5917.         error_mess = post_handoff(header, body, error_buf);
  5918.     }
  5919.  
  5920.     if(we_cancel)
  5921.       cancel_busy_alarm(0);
  5922.  
  5923.     if(error_mess){
  5924.     if(local_so && !local_written)
  5925.       so_give(&local_so);    /* clean up any fcc data */
  5926.  
  5927.         q_status_message(SM_ORDER | SM_DING, 4, 5, error_mess);
  5928.         return(-1);
  5929.     }
  5930.  
  5931.     local_written = 1;
  5932.     return(1);
  5933. }
  5934.  
  5935.  
  5936.  
  5937. /*
  5938.  * view_as_rich - set the rich_header flag
  5939.  *
  5940.  *         name  - name of the header field
  5941.  *         deflt - default value to return if user didn't set it
  5942.  *
  5943.  *         Note: if the user tries to turn them all off with "", then
  5944.  *         we take that to mean default, since otherwise there is no
  5945.  *         way to get to the headers.
  5946.  */
  5947. int
  5948. view_as_rich(name, deflt)
  5949. char *name;
  5950. int deflt;
  5951. {
  5952.     char **p;
  5953.     char *q;
  5954.  
  5955.     p = ps_global->VAR_COMP_HDRS;
  5956.  
  5957.     if(p && *p && **p){
  5958.         for(; (q = *p) != NULL; p++){
  5959.         if(!struncmp(q, name, strlen(name)))
  5960.           return 0; /* 0 means we *do* view it by default */
  5961.     }
  5962.  
  5963.         return 1; /* 1 means it starts out hidden */
  5964.     }
  5965.     return(deflt);
  5966. }
  5967.  
  5968.  
  5969. /*
  5970.  * is_a_forbidden_hdr - is this name a "forbidden" header?
  5971.  *
  5972.  *          name - the header name to check
  5973.  *  We don't allow user to change these.
  5974.  */
  5975. int
  5976. is_a_forbidden_hdr(name)
  5977. char *name;
  5978. {
  5979.     char **p;
  5980.     static char *forbidden_headers[] = {
  5981.     "sender",
  5982.     "x-sender",
  5983.     "date",
  5984.     "received",
  5985.     "message-id",
  5986.     "in-reply-to",
  5987.     "path",
  5988.     "resent-message-id",
  5989.     "resent-message-date",
  5990.     "resent-message-from",
  5991.     "resent-message-sender",
  5992.     "resent-message-to",
  5993.     "resent-message-cc",
  5994.     "resent-message-reply-to",
  5995.     NULL
  5996.     };
  5997.  
  5998.     for(p = forbidden_headers; *p; p++)
  5999.       if(!strucmp(name, *p))
  6000.     break;
  6001.  
  6002.     return((*p) ? 1 : 0);
  6003. }
  6004.  
  6005.  
  6006. /*
  6007.  * count_custom_hdrs - returns number of custom headers defined by user
  6008.  */
  6009. int
  6010. count_custom_hdrs()
  6011. {
  6012.     char **p;
  6013.     char  *q     = NULL;
  6014.     char  *name;
  6015.     char  *t;
  6016.     char   save;
  6017.     int    ret   = 0;
  6018.     /* how many fixed fields are there? */
  6019.     int    fixed_cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  6020.  
  6021.     if(ps_global->VAR_CUSTOM_HDRS){
  6022.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6023.         if(q[0]){
  6024.         /* remove leading whitespace */
  6025.             for(name = q; *name && isspace(*name); name++)
  6026.               ;/* do nothing */
  6027.         
  6028.         /* look for colon or space or end */
  6029.             for(t = name; *t && !isspace(*t) && *t != ':'; t++)
  6030.               ;/* do nothing */
  6031.  
  6032.         save = *t;
  6033.         *t = '\0';
  6034.         if(!is_a_std_hdr(name) && !is_a_forbidden_hdr(name))
  6035.           ret++;
  6036.  
  6037.         *t = save;
  6038.         }
  6039.     }
  6040.     }
  6041.  
  6042.     return(ret);
  6043. }
  6044.  
  6045.  
  6046. /*
  6047.  * set_default_hdrval - put the user's default value for this header
  6048.  *                      into pf->textbuf.
  6049.  */
  6050. void
  6051. set_default_hdrval(pf)
  6052. PINEFIELD *pf;
  6053. {
  6054.     char       **p = 0;
  6055.     char        *q = 0;
  6056.     char        *t;
  6057.     char        *name;
  6058.     char        *value;
  6059.     char         save;
  6060.  
  6061.     if(!pf || !pf->name){
  6062.     q_status_message(SM_ORDER,3,7,"Internal error setting default header");
  6063.     return;
  6064.     }
  6065.  
  6066.     pf->textbuf = NULL;
  6067.  
  6068.     if(ps_global->VAR_CUSTOM_HDRS){
  6069.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6070.  
  6071.         if(q[0]){
  6072.  
  6073.         /* remove leading whitespace */
  6074.             for(name = q; *name && isspace(*name); name++)
  6075.               ;/* do nothing */
  6076.         
  6077.         if(!*name)
  6078.           continue;
  6079.  
  6080.         /* look for colon or space or end */
  6081.             for(t = name; *t && !isspace(*t) && *t != ':'; t++)
  6082.               ;/* do nothing */
  6083.  
  6084.         save = *t;
  6085.         *t = '\0';
  6086.  
  6087.         if(strucmp(name, pf->name) != 0){
  6088.             *t = save;
  6089.             continue;
  6090.         }
  6091.  
  6092.         /* turn on editing */
  6093.         if(strucmp(name, "From") == 0
  6094.           || strucmp(name, "Reply-To") == 0)
  6095.           pf->canedit = 1;
  6096.  
  6097.         *t = save;
  6098.  
  6099.         /* remove space between name and colon */
  6100.             for(value = t; *value && isspace(*value); value++)
  6101.               ;/* do nothing */
  6102.  
  6103.         if(*value && *value == ':'){
  6104.             /* remove leading whitespace from default value */
  6105.             for(++value; *value && isspace(*value); value++)
  6106.               ;/* do nothing */
  6107.  
  6108.             pf->textbuf = cpystr(value);
  6109.         }
  6110.  
  6111.         break;
  6112.         }
  6113.     }
  6114.     }
  6115.  
  6116.     if(!pf->textbuf)
  6117.       pf->textbuf = cpystr("");
  6118. }
  6119.  
  6120.  
  6121. /*
  6122.  * is_a_std_hdr - is this name a "standard" header?
  6123.  *
  6124.  *          name - the header name to check
  6125.  */
  6126. int
  6127. is_a_std_hdr(name)
  6128. char *name;
  6129. {
  6130.     int    i;
  6131.     /* how many standard headers are there? */
  6132.     int fixed_cnt = (sizeof(pf_template)/sizeof(pf_template[0])) - 1;
  6133.  
  6134.     /* check to see if this is a standard header */
  6135.     for(i = 0; i < fixed_cnt; i++)
  6136.       if(!strucmp(name, pf_template[i].name))
  6137.     break;
  6138.  
  6139.     return((i < fixed_cnt) ? 1 : 0);
  6140. }
  6141.  
  6142.  
  6143. /*
  6144.  * customized_hdr_setup - setup the PINEFIELDS for all the customized headers
  6145.  *                    Allocates space for each name and addr ptr.
  6146.  *                    Allocates space for default in textbuf, even if empty.
  6147.  *
  6148.  *              head - the first PINEFIELD to fill in
  6149.  */
  6150. void
  6151. customized_hdr_setup(head)
  6152. PINEFIELD *head;
  6153. {
  6154.     char **p, *q, *t, *name, *value, save;
  6155.     int forbid;
  6156.     PINEFIELD *pf;
  6157.  
  6158.     pf = head;
  6159.  
  6160.     if(ps_global->VAR_CUSTOM_HDRS){
  6161.         for(p = ps_global->VAR_CUSTOM_HDRS; (q = *p) != NULL; p++){
  6162.  
  6163.         if(q[0]){
  6164.  
  6165.         /* remove leading whitespace */
  6166.             for(name = q; *name && isspace(*name); name++)
  6167.               ;/* do nothing */
  6168.         
  6169.         if(!*name)
  6170.           continue;
  6171.  
  6172.         /* look for colon or space or end */
  6173.             for(t = name; *t && !isspace(*t) && *t != ':'; t++)
  6174.               ;/* do nothing */
  6175.  
  6176.         save = *t;
  6177.         *t = '\0';
  6178.  
  6179.         /*
  6180.          * The same pinerc variable is used to customize standard
  6181.          * headers, so skip them here.  Also, don't allow
  6182.          * any of the forbidden headers.
  6183.          */
  6184.         forbid = 0;
  6185.         if(is_a_std_hdr(name) || (forbid=is_a_forbidden_hdr(name))){
  6186.             if(forbid)
  6187.               q_status_message1(SM_ORDER, 3, 3,
  6188.                 "Not allowed to change header \"%s\"", name);
  6189.  
  6190.             *t = save;
  6191.             continue;
  6192.         }
  6193.  
  6194.         pf->name = cpystr(name);
  6195.         pf->type = FreeText;
  6196.         pf->next = pf+1;
  6197.         /*
  6198.          * For now, all custom headers are FreeText except for
  6199.          * this one that we happen to know about.  We might
  6200.          * have to add some syntax to the config option so that
  6201.          * people can tell us their custom header takes addresses.
  6202.          */
  6203.         if(!strucmp(pf->name, "Return-Receipt-to")){
  6204.             pf->type = Address;
  6205.              pf->addr = (ADDRESS **)fs_get(sizeof(ADDRESS *));
  6206.             *pf->addr = (ADDRESS *)NULL;
  6207.         }
  6208.  
  6209.         *t = save;
  6210.  
  6211.         /* remove space between name and colon */
  6212.             for(value = t; *value && isspace(*value); value++)
  6213.               ;/* do nothing */
  6214.  
  6215.         /* give them an alloc'd default, even if empty */
  6216.         if(!*value || (*value && *value != ':'))
  6217.           pf->textbuf = cpystr("");
  6218.         else{
  6219.             /* remove leading whitespace from default value */
  6220.             for(++value; *value && isspace(*value); value++)
  6221.               ;/* do nothing */
  6222.  
  6223.             pf->textbuf = cpystr(value);
  6224.         }
  6225.  
  6226.         pf++;
  6227.         }
  6228.     }
  6229.     }
  6230.  
  6231.     /* fix last next pointer */
  6232.     if(pf != head)
  6233.       (pf-1)->next = NULL;
  6234. }
  6235.  
  6236.  
  6237. /*
  6238.  * get_dflt_custom_hdrs - allocate PINEFIELDS for custom headers
  6239.  *                        fill in the defaults
  6240.  */
  6241. PINEFIELD *
  6242. get_dflt_custom_hdrs()
  6243. {
  6244.     PINEFIELD          *pfields, *pf;
  6245.     int            i;
  6246.  
  6247.     /* add one for possible use by fcc */
  6248.     i       = (count_custom_hdrs() + 2) * sizeof(PINEFIELD);
  6249.     pfields = (PINEFIELD *)fs_get((size_t) i);
  6250.     memset(pfields, 0, (size_t) i);
  6251.  
  6252.     /* set up the custom header pfields */
  6253.     customized_hdr_setup(pfields);
  6254.  
  6255.     return(pfields);
  6256. }
  6257.  
  6258.  
  6259. /*
  6260.  * free_customs - free misc. resources associated with custom header fields
  6261.  *
  6262.  *           pf - pointer to first custom field
  6263.  */
  6264. void
  6265. free_customs(head)
  6266. PINEFIELD *head;
  6267. {
  6268.     PINEFIELD *pf;
  6269.  
  6270.     for(pf = head; pf && pf->name; pf = pf->next){
  6271.  
  6272.     fs_give((void **)&pf->name);
  6273.  
  6274.     /* only true for FreeText */
  6275.     if(pf->textbuf)
  6276.       fs_give((void **)&pf->textbuf);
  6277.  
  6278.     /* only true for Address */
  6279.     if(pf->addr){
  6280.         if(*pf->addr)
  6281.           mail_free_address(pf->addr);
  6282.  
  6283.         fs_give((void **)&pf->addr);
  6284.     }
  6285.  
  6286.     if(pf->he && pf->he->prompt)
  6287.       fs_give((void **)&pf->he->prompt);
  6288.     }
  6289.  
  6290.     fs_give((void **)&head);
  6291. }
  6292.  
  6293.  
  6294.  
  6295. /**************** "PIPE" READING POSTING I/O ROUTINES ****************/
  6296.  
  6297. /* 
  6298.  * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
  6299.  *             pipes rather than a tcp stream
  6300.  */
  6301. long
  6302. pine_pipe_soutr (stream,s)
  6303.      void *stream;
  6304.      char *s;
  6305. {
  6306.     long rv = 0;
  6307.  
  6308.     if(*s)
  6309.       do
  6310.     rv = fwrite(s,strlen(s),(size_t)1,((PIPE_S *)stream)->ofilep);
  6311.       while(!rv && ferror(((PIPE_S *)stream)->ofilep) && errno == EINTR);
  6312.     else
  6313.       rv = T;
  6314.  
  6315.     return(rv);
  6316. }
  6317.  
  6318.  
  6319. /* 
  6320.  * pine_pipe_getline - Replacement for tcp_getline that reads one
  6321.  *               of our pipes rather than a tcp pipe
  6322.  */
  6323. char *
  6324. pine_pipe_getline (stream)
  6325.     void *stream;
  6326. {
  6327.     char tmp[4 * MAILTMPLEN];
  6328.     char *p = NULL;
  6329.  
  6330.     fflush(((PIPE_S *)stream)->ofilep);
  6331.     do
  6332.       p = fgets(tmp, 4 * MAILTMPLEN, ((PIPE_S *)stream)->ifilep);
  6333.     while(!p && ferror(((PIPE_S *)stream)->ifilep) && errno == EINTR);
  6334.  
  6335.     return(p ? cpystr(tmp) : p);
  6336. }
  6337.  
  6338.  
  6339. /* 
  6340.  * pine_pipe_close - Replacement for tcp_close that closes pipes to our
  6341.  *             child rather than a tcp connection
  6342.  */
  6343. void
  6344. pine_pipe_close(stream)
  6345.     void *stream;
  6346. {
  6347.     PIPE_S *p = (PIPE_S *)stream;
  6348.  
  6349.     fclose(p->ofilep);
  6350.     p->ofilep = NULL;
  6351.     (void) close_system_pipe(&p);
  6352. }
  6353.